Java与CSP数据兼容之三:Java兼容CSP的DES/3DES密钥数据和密文

来源:互联网 发布:东莞虎门淘宝摄影 编辑:程序博客网 时间:2024/06/05 16:25
         在数据安全应用中,往往需要将客户端由C++调用CryptoAPI生成的密文、传至服务器端的Java代码解密,当然也有相反的操作。这时就需要清楚Windows CryptoAPI和Java安全库之间的差异,以便密钥数据和密文数据格式能兼容。下面将介绍该过程中的几个注意事项。

一、密钥数据块的解析

  在Windows平台上,C++代码调用CryptoAPI函数CryptExportKey()得到的对称密钥数据结构为PLAINTEXTKEYBLOB类型,该数据块的结构为:“BLOBHEADER + 密钥数据长度 + 密钥数据“,其中”密钥数据长度“为4个字节。在Java代码中,需要解析该数据库,得到密钥数据,用来创建对称密钥对象。

二、加密模式保持一致

  对称密钥加密,常用的有ECB和CBC两种模式,CryptoAPI函数CryptEncrypt()用来使用密钥加密数据时,如果没有对使用的密钥做模式设置,则默认使用CBC模式,MSDN中的介绍如下:

而Java的安全库中,默认时使用的ECB模式。所以需要保证两边使用的是同一种加密模式,需要通过设置来完成。可以通过C++代码调用CryptSetKeyParam()来设置CryptoAPI的加密模式,也可以在Java中指定明确的加密模式。详细代码见后面的例程。

三、加密参数保持一致

  如果使用CBC模式加密,这需要指定加密时使用的向量IV。在使用CryptoAPI时,如果没有明确指定IV的值,则默认使用8个0作为IV。MSDN中的说明如下:

四、密文填充模式保持一致

  由于加密时数据不一定为密钥密文块的整数倍,所以往往需要对密文进行补齐。CryptoAPI只支持PKCS5的补齐方式,所以在Java中也需要使用PKCS5补齐模式。关于CryptoAPI的补齐说明,MSDN说明如下:

五、加密文本时使用UTF-8编码

  由于Java使用的UTF-8字符编码,所以在对文本字符加密时,C++代码需要将字符串需要按UTF-8字符集转化为BYTE[]数组,再进行加密,否则Java端解密成功后,显示文本时中文(或者其他多字节字符)时会是乱码。当然,如果只是对二进制数据加解密,则无需考虑这点。


基于以上说明过,完整地C++加密、Java解密的代码如下:

C++加密代码:

    #include "stdafx.h"      #include <windows.h>      #include <atlconv.h>      #include "..\Common\Base64.h"            void Reverse(LPBYTE lpData, DWORD dwLen);      void PrintDataInHex(LPBYTE lpData, DWORD dwLen);      void PrintDataInBase64(LPBYTE lpData, DWORD dwLen);            int _tmain(int argc, _TCHAR* argv[])      {          ULONG ulRes = 0;          ULONG ulKeyLen = 0;          ULONG ulPlainDataLen = 0;          ULONG ulCipherLen = 256;          LPBYTE lpbtKeyData = NULL;            LPBYTE lpbtPlainData = NULL;          BYTE btCipherData[256] = {0};          HCRYPTPROV hProv = NULL;          HCRYPTKEY hKey = NULL;          const TCHAR tcCSPName[] = _T("Microsoft Enhanced Cryptographic Provider v1.0");           const TCHAR tcPlainText[] = _T("This is plian text!这是明文数据!");                          // 打开CSP              if (!CryptAcquireContext(&hProv, NULL, tcCSPName, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT))          {              ulRes = GetLastError();              printf("CryptAcquireContext() failed! ulRes = 0x%x\n", ulRes);              goto END;          }                // 生成3DES密钥          if (!CryptGenKey(hProv, CALG_3DES, 0x00A80000|CRYPT_EXPORTABLE, &hKey) || !hKey)          {              ulRes = GetLastError();              printf("CryptGenKey() failed! ulRes = 0x%x\n", ulRes);              goto END;          }                // 导出3DES密钥数据(明文)          if (!CryptExportKey(hKey, NULL, PLAINTEXTKEYBLOB, NULL, NULL, &ulKeyLen) || ulKeyLen == 0)          {              ulRes = GetLastError();              printf("CryptExportKey() failed! ulRes = 0x%x\n", ulRes);              goto END;          }          lpbtKeyData = new BYTE[ulKeyLen];          if (!CryptExportKey(hKey, NULL, PLAINTEXTKEYBLOB, NULL, lpbtKeyData, &ulKeyLen))          {              printf("CryptExportKey() failed! ulRes = 0x%x\n", ulRes);              goto END;          }          printf("3DES Key data:\n");          PrintDataInHex(lpbtKeyData, ulKeyLen);          printf("\n");          PrintDataInBase64(lpbtKeyData, ulKeyLen);                    // 原文数据          ULONG ulLen = WideCharToMultiByte(CP_UTF8, 0, tcPlainText, wcslen(tcPlainText), NULL, 0, NULL, NULL);          lpbtPlainData = new BYTE[ulLen + 1];          memset(lpbtPlainData, 0, ulLen + 1);           ulLen = WideCharToMultiByte(CP_UTF8, 0, tcPlainText, wcslen(tcPlainText), (LPSTR)lpbtPlainData, ulLen+1, NULL, NULL);          ulPlainDataLen = ulLen;          printf("\nPlain text data:\n");          PrintDataInHex(lpbtPlainData, ulPlainDataLen);                // 加密数据          ulCipherLen = ulPlainDataLen;          memcpy(btCipherData, lpbtPlainData, ulPlainDataLen);          if (!CryptEncrypt(hKey, NULL, TRUE, 0, (LPBYTE)btCipherData, &ulCipherLen, 256))          {              ulRes = GetLastError();              printf("CryptEncrypt() failed! ulRes = 0x%x\n", ulRes);              goto END;          }          printf("\nCipher data:\n");          PrintDataInHex(btCipherData, ulCipherLen);          printf("\n");          PrintDataInBase64(btCipherData, ulCipherLen);            END:          if (lpbtKeyData)          {              delete []lpbtKeyData;              lpbtKeyData = NULL;          }          if (hKey)          {              CryptDestroyKey(hKey);              hKey = NULL;          }          if (hProv)          {              CryptReleaseContext(hProv, 0);              hProv = NULL;          }                printf("\n");          getchar();          return 0;      }            void Reverse(LPBYTE lpData, DWORD dwLen)      {          BYTE temp = 0;          for (ULONG i = 0; i < dwLen / 2; i++)          {              temp = lpData[i];              lpData[i] = lpData[dwLen - (i + 1)];              lpData[dwLen - ( i + 1)] = temp;          }      }            void PrintDataInHex(LPBYTE lpData, DWORD dwLen)      {          for (DWORD dwIndex = 0; dwIndex < dwLen; dwIndex++)          {              printf("%02X ", lpData[dwIndex]);              if ((dwIndex + 1) % 16 == 0)              {                  printf("\n");              }          }      }            void PrintDataInBase64(LPBYTE lpData, DWORD dwLen)      {          ULONG ulBase64Len = 0;          LPTSTR lptsBase64 = NULL;                ulBase64Len = BinaryToBase64(lpData, dwLen, NULL);          if (0 == ulBase64Len)          {              printf("BinaryToBase64() failed!\n");              return;          }                lptsBase64 = new TCHAR[ulBase64Len + 1];          ulBase64Len = BinaryToBase64(lpData, dwLen, lptsBase64);          if (0 == ulBase64Len)          {              printf("BinaryToBase64() failed!\n");              goto END;          }                _tprintf(_T("%s\n"), lptsBase64);            END:          if (lptsBase64)          {              delete []lptsBase64;              lptsBase64 = NULL;          }      }  

以上C++代码输出结果如下图:



    /**      * Created by Singler on 2015/10/29.      * DES/3DES加解密实现类      *      */      public class DES {          static ALG _keyAlg = ALG.ALG_DES;          final static byte[] _IV = {0, 0, 0, 0, 0, 0, 0, 0};                enum ALG {ALG_DES, ALG_3DES};          enum MODE {ECB, CBC, CFB, OFB, CTS};                /**          * 创建一个DES/3DES对称密钥对象          *          * */          public static SecretKey CreateKey(byte[] keyData, ALG alg) {              SecretKey key = null;              String algName = "DES";              try {                  /** 生成密钥对象 */                  if (ALG.ALG_DES == alg) {                      algName = "DES";                      _keyAlg = ALG.ALG_DES;                  }                  else if (ALG.ALG_3DES == alg) {                      algName = "DESede";                      _keyAlg = ALG.ALG_3DES;                  }                  else {                      return null;                  }                  key = new SecretKeySpec(keyData, algName);              } catch(java.lang.Exception e3){                  e3.printStackTrace();              }              return key;          }                /**          * 使用一个DES/3DES密钥解密数据          *          * */          public static byte[] decrypt(SecretKey key, MODE mode, byte[] src) {              Cipher cipher = null;              IvParameterSpec ivp = new IvParameterSpec(_IV);                    try {                  /** 根据不同模式,初始化密文对象 */                  if (MODE.ECB == mode) {                      if (ALG.ALG_3DES == _keyAlg) {                          cipher = Cipher.getInstance("DESede/ECB/PKCS5Padding");                      }                      else {                          cipher = Cipher.getInstance("DES/ECB/PKCS5Padding");                      }                      cipher.init(Cipher.DECRYPT_MODE, key);                  }                  else if (MODE.CBC == mode) {                      if (ALG.ALG_3DES == _keyAlg) {                          cipher = Cipher.getInstance("DESede/CBC/PKCS5Padding");                      }                      else {                          cipher = Cipher.getInstance("DES/CBC/PKCS5Padding");                      }                      cipher.init(Cipher.DECRYPT_MODE, key, ivp);                  }                        /** 返回解密结果 */                  return cipher.doFinal(src);              } catch (java.security.NoSuchAlgorithmException e1) {                  // TODO: handle exception                  e1.printStackTrace();              }catch(javax.crypto.NoSuchPaddingException e2){                  e2.printStackTrace();              }catch(java.lang.Exception e3){                  e3.printStackTrace();              }              return null;          }      }            public class Main {          static final String cipherIn3Des = "rg0RdqDohH4Yiu1Eej6+r2cuYo9ilVPdO/a+VLb+AeXG1J8cyNygF+y42ijJhe6q";          static final String keyDataFor3Des = "CAIAAANmAAAYAAAAyKTvWyz4E+W5aMQ+Q+nHMhPpLzvy9Dtn";                /** CryptoAPI导出的SesssionKey数据块中块头的长度 = sizeof(BLOBHEADER) */          static final int _KeyBlobHeaderSize = 8;                public static void main(String[] args) {                    try {                  int len = 0;                  byte[] lenData = new byte[4];                        //从CSP导出的PLAINTEXTKEYBLOB数据块中获取3DES密钥数据                  byte[] keyBlobData = Base64.getDecoder().decode(keyDataFor3Des);                  System.arraycopy(keyBlobData, _KeyBlobHeaderSize, lenData, 0, 4);                  len = Convert.toInt(lenData);                  byte[] keyData = new byte[len];                  System.arraycopy(keyBlobData, _KeyBlobHeaderSize + 4, keyData, 0, len);                        //解密数据                  byte[] cipherData = Base64.getDecoder().decode(cipherIn3Des);                  SecretKey key = DES.CreateKey(keyData, DES.ALG.ALG_3DES);                  byte[] decrypted = DES.decrypt(key, DES.MODE.CBC, cipherData);                        //解密后的数据(16进制显示)                  System.out.println("Decrypted data:");                  String decryptedData = Convert.bytesToHexString(decrypted);                  System.out.println(decryptedData);                        //解密后的数据(文本显示)                  System.out.println("Decrypted text:");                  String decryptedStr = new String(decrypted);                  System.out.println(decryptedStr);              }              catch (Exception e) {                  System.out.println(e.getMessage());              }          }      }  

以上Java代码输出结果如下图:


阅读全文
1 0
原创粉丝点击