Java与CSP数据兼容之三:Java兼容CSP的DES/3DES密钥数据和密文
来源:互联网 发布:东莞虎门淘宝摄影 编辑:程序博客网 时间:2024/06/05 16:25
一、密钥数据块的解析
在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代码输出结果如下图:
- Java与CSP数据兼容之三:Java兼容CSP的DES/3DES密钥数据和密文
- Java与CSP数据兼容之三:Java兼容CSP的DES/3DES密钥数据和密文
- Java与CSP数据兼容之二:Java兼容CSP导出的RSA私钥数据
- Java与CSP数据兼容之二:Java兼容CSP导出的RSA私钥数据
- Java与CSP数据兼容之一:Java兼容CSP导出的RSA公钥数据
- Java与CSP数据兼容之一:Java兼容CSP导出的RSA公钥数据
- 与java兼容的delphi xe实现des算法单元
- PHP的DES加解密函数 与JAVA兼容
- android实现DES算法和java不兼容的问题解决...
- 兼容PHP和Java的des加密解密代码分享
- Java中3DES加密与C#兼容
- Java中3DES加密与C#兼容
- php 的 3des加解密类,兼容C#/java
- 用Java实现的单倍长密钥DES、双倍长密钥3DES和Mac计算
- 用Java实现的单倍长密钥DES、双倍长密钥3DES和Mac计算
- 【密钥算法】Java加密技术(二)---DES数据加密算法
- PHP之——DES加解密函数 与JAVA兼容(插曲)
- CSP之 Markdown java
- 4用于cifar10的卷积神经网络-4.8/4.9为cifar10卷积网络添加汇总操作上/下
- 利用python进行数据分析学习笔记-Pandas篇
- Struts 多个文件,拦截器,文件上传
- 【学习笔记】设计模式-适配器模式
- leetcode 53. Maximum Subarray
- Java与CSP数据兼容之三:Java兼容CSP的DES/3DES密钥数据和密文
- response重定向的网址中有中文乱码的解决办法
- linux regular expression
- 【JQuery学习笔记三】JQuery学习总结
- Quartz定时任务(附demo)(一)
- git checkout
- NOIP 2017 小凯的疑惑 (数学)
- 产品的一点思考
- 文件