OpenSSL与CryptoAPI交互AES加密解密

继上次只有CryptoAPI的加密后,这次要实现openssl的了。

动机:利用CryptoAPI制作windows的IE,火狐和chrome加密控件后,这次得加上与Android的加密信息交互。

先前有说openssl移植到android的过程,这里就不再提android如何调用openssl了,而那一篇第9条提到的openssl与cryptoAPI兼容的两种方式感觉实现都不太好用,这里再次提出一种AES加密的实现方式。

写这边文章的最主要的原因,用过CryptoAPI的都知道,很多东西都封装了,如果要与其他加密组件交互,得用其他组件来实现CryptoAPI的思路。

环境:windows visual studio 2010,openssl windows(x86)动态库。

在CryptoAPI中进行AES加密解密,有一种实现方式是调用CryptDeriveKey通过提供的字节数组的hash值获取key。

先来看下CryptoAPI实现AES,来个简单点的版本。
1.void cryptoAPI_encrypt(string text,unsigned char* pwd,unsigned char** encryptText,int &out_len)
2.{
3. HCRYPTPROV hCryptProv = NULL;
4. HCRYPTKEY hKey = 0;
5. HCRYPTHASH hHash = 0;
6. int dwLength = 0;
7.
8. if(!CryptAcquireContext(&hCryptProv,
9. NULL,
10. CSP_NAME,//CSP_NAME
11. PROV_RSA_AES,
12. CRYPT_VERIFYCONTEXT))
13. {
14. DWORD dwLastErr = GetLastError();
15.
16. if(NTE_BAD_KEYSET == dwLastErr)
17. {
18. return;
19. }
20. else{
21. if(!CryptAcquireContext(&hCryptProv,
22. NULL,
23. CSP_NAME,
24. PROV_RSA_AES,
25. CRYPT_NEWKEYSET))
26. {
27. return;
28. }
29. }
30. }
31. if(!CryptCreateHash(hCryptProv, CALG_MD5, 0, 0, &hHash))
32. {
33. return;
34. }
35.
36. BYTE *pPwd = pwd;
37.
38. if(!CryptHashData(hHash, pPwd, 16, 0))
39. {
40. return;
41. }
42.
43. if(!CryptDeriveKey(hCryptProv, CALG_AES_128, hHash, CRYPT_EXPORTABLE, &hKey))
44. {
45. return;
46. }
47.
48. int len = text.length();
49. BYTE *pData ;
50. pData = (BYTE*)malloc(len*4);
51. memcpy(pData,text.c_str(),len);
52. DWORD dwLen = len;
53.
54. if(!CryptEncrypt(hKey, NULL, true, 0, pData, &dwLen, len*4))
55. {
56. return;
57. }
58.
59. cout <<"--------------------------" << endl << "cryptoAPI encrypt"<<endl;
60. printBytes(pData,dwLen);
61. *encryptText = pData;
62. out_len = dwLen;
63. CryptDestroyHash(hHash);
64. CryptDestroyKey(hKey);
65. CryptReleaseContext(hCryptProv,0);
66.}
这里将传进来的字节数组密钥先进行MD5摘要后,再通过CryptoDeriveKey来得到最后用来加密的密钥

openssl要以同样的方式做一次这个步骤,首先是MD5摘要,相对比较简单

1.unsigned char* openssl_md5(unsigned char*sessionKey,size_t n)
2.{
3. unsigned char *ret = (unsigned char*)malloc(MD5_DIGEST_LENGTH);
4. MD5(sessionKey,n,ret);
5. return ret;
6.}
然后再来实现CryptoDeriveKey,先来看下MSDN上对于这个函数的说明

主要看remarks里面的实现步骤:

1.Let n be the required derived key length, in bytes. The derived key is the first n bytes of the hash value after the hash computation has been completed by CryptDeriveKey. If the hash is not a member of the SHA-2 family and the required key is for either 3DES or AES, the key is derived as follows:
2.
3.1.Form a 64-byte buffer by repeating the constant 0x36 64 times. Let k be the length of the hash value that is represented by the input parameter hBaseData. Set the first k bytes of the buffer to the result of an XOR operation of the first k bytes of the buffer with the hash value that is represented by the input parameter hBaseData.
4.2.Form a 64-byte buffer by repeating the constant 0x5C 64 times. Set the first k bytes of the buffer to the result of an XOR operation of the first k bytes of the buffer with the hash value that is represented by the input parameter hBaseData.
5.3.Hash the result of step 1 by using the same hash algorithm as that used to compute the hash value that is represented by the hBaseData parameter.
6.4.Hash the result of step 2 by using the same hash algorithm as that used to compute the hash value that is represented by the hBaseData parameter.
7.5.Concatenate the result of step 3 with the result of step 4.
8.6.Use the first n bytes of the result of step 5 as the derived key.

非常简单的英文,不做翻译了...


直接上openssl代码实现

1.//参见 http://msdn.microsoft.com/en-us/library/aa379916(v=vs.85).aspx remarks步骤
2.unsigned char* derivedKey(unsigned char*sessionKey/*hash后的值*/,size_t n/*密钥长度*/)
3.{
4. /**step 1*/
5. unsigned char* buffer = (unsigned char*)malloc(64);
6.
7. for(int i = 0 ; i < 64;i++)
8. {
9. buffer[i] = 0x36;
10. }
11.
12. int k = n;
13.
14. for(int i = 0 ; i < k ; i++)
15. {
16. buffer[i] = buffer[i] ^ sessionKey[i];
17. }
18.
19. /*step 2*/
20. unsigned char* buffer2 = (unsigned char*)malloc(64);
21.
22. for(int i = 0 ; i < 64;i++)
23. {
24. buffer2[i] = 0x5C;
25. }
26.
27. for(int i = 0 ; i < k ; i++)
28. {
29. buffer2[i] = buffer2[i] ^ sessionKey[i];
30. }
31. /*step 3*/
32. unsigned char* ret1 = openssl_md5(buffer,64);
33. /*step 4*/
34. unsigned char* ret2 = openssl_md5(buffer2,64);
35.
36.
37. unsigned char* ret = (unsigned char*)malloc(128);
38. for(int i = 0 ; i < 128;i++)
39. {
40. if(i<64)
41. ret[i] = ret1[i];
42. else
43. ret[i] = ret2[i-64];
44. }
45. return ret;
46.}
最麻烦的地方解决了...剩下再按照CryptoAPI的实现顺序实现吧
1.void openssl_aes_encrypt(string text,unsigned char** SessionKey_out/*这里主要用作将产生的对称密钥输出*/,unsigned char* sEncryptMsg,int &len)
2.{
3. OpenSSL_add_all_algorithms();
4. //产生会话密钥
5. *SessionKey_out = (unsigned char*)malloc(MD5_SIZE);
6. RAND_bytes(*SessionKey_out,MD5_SIZE);//产生随机密钥,输出之后可以给其他方法是用了
7. unsigned char* SessionKey = openssl_md5(*SessionKey_out,MD5_SIZE);
8.
9. SessionKey = derivedKey(SessionKey,MD5_SIZE);
10.
11. const unsigned char* sMsg = (const unsigned char*)text.c_str();
12. int cbMsg = text.length();
13. int cbEncryptMsg;
14. //加密
15. EVP_CIPHER_CTX ctx;
16. EVP_CIPHER_CTX_init(&ctx);
17. if(EVP_EncryptInit_ex(&ctx,EVP_get_cipherbynid(NID_aes_128_cbc),NULL,SessionKey,NULL))
18. {
19. int offseti=0;//in
20. int offseto=0;//out
21. int offsett=0;//temp
22. for(;;)
23. {
24. if(cbMsg-offseti<=MAX_ENCRYPT_LEN)
25. {
26. EVP_EncryptUpdate(&ctx, sEncryptMsg+offseto, &offsett, sMsg+offseti, cbMsg-offseti);
27. offseto+=offsett;
28. break;
29. }
30. else
31. {
32. EVP_EncryptUpdate(&ctx, sEncryptMsg+offseto, &offsett, sMsg+offseti, MAX_ENCRYPT_LEN);
33. offseti+=MAX_ENCRYPT_LEN;
34. offseto+=offsett;
35. }
36. }
37. EVP_EncryptFinal_ex(&ctx, sEncryptMsg+offseto, &offsett);
38. offseto+=offsett;
39. cbEncryptMsg=offseto;
40. }
41. EVP_CIPHER_CTX_cleanup(&ctx);
42. std::cout << "openssl encrypt:" << std::endl;
43. printBytes(sEncryptMsg,cbEncryptMsg);
44. len = cbEncryptMsg;
45.}
加密的搞定了,可以尝试下了,同样的密钥和同样的明文,密文输出结果是一样的就对了

下面是CrytpoAPI的解密:

1.void cryptAPI_decrypt(unsigned char* text,int len,unsigned char* pwd)
2.{
3. HCRYPTPROV hCryptProv = NULL;
4. HCRYPTKEY hKey = 0;
5. HCRYPTHASH hHash = 0;
6. int dwLength = 0;
7.
8. if(!CryptAcquireContext(&hCryptProv,
9. NULL,
10. CSP_NAME,//CSP_NAME
11. PROV_RSA_AES,
12. CRYPT_VERIFYCONTEXT))
13. {
14. DWORD dwLastErr = GetLastError();
15.
16. if(NTE_BAD_KEYSET == dwLastErr)
17. {
18. return;
19. }
20. else{
21. if(!CryptAcquireContext(&hCryptProv,
22. NULL,
23. CSP_NAME,
24. PROV_RSA_AES,
25. CRYPT_NEWKEYSET))
26. {
27. return;
28. }
29. }
30. }
31.
32.
33. if(!CryptCreateHash(hCryptProv, CALG_MD5, 0, 0, &hHash))
34. {
35. return;
36. }
37.
38. BYTE *pPwd = pwd;
39.
40. if(!CryptHashData(hHash, pPwd, 16, 0))
41. {
42. return;
43. }
44.
45. if(!CryptDeriveKey(hCryptProv, CALG_AES_128, hHash, CRYPT_EXPORTABLE, &hKey))
46. {
47. return;
48. }
49.
50. BYTE *pData = text;
51. DWORD dwLen = len;
52.
53. if(!CryptDecrypt(hKey, NULL, true, 0, pData, &dwLen))
54. {
55. return;
56. }
57.
58. cout <<"--------------------------" << endl << "cryptoAPI decrypt"<<endl;
59. char* plainText = (char*)malloc(dwLen + 1);
60. memcpy(plainText,pData,dwLen);
61. plainText[dwLen] = '\0';
62. cout << plainText << endl;
63. CryptDestroyHash(hHash);
64. CryptDestroyKey(hKey);
65. CryptReleaseContext(hCryptProv,0);
66.}
尝试用这个方法解密CryptoAPI的加密和openssl的加密吧,都能输出明文的

再来最后一个,openssl的解密

1.void openssl_aes_decrypt(unsigned char* text,int len,unsigned char* SessionKeyP)
2.{
3. unsigned char* decryptMsg = (unsigned char*)malloc(len);
4.
5. OpenSSL_add_all_algorithms();
6.
7. unsigned char* SessionKey = openssl_md5(SessionKeyP,MD5_SIZE);
8.
9. SessionKey = derivedKey(SessionKey,MD5_SIZE);
10.
11. const unsigned char* sMsg = text;
12. int cbMsg = len;
13. int cbEncryptMsg;
14. //解密
15. EVP_CIPHER_CTX ctx;
16. EVP_CIPHER_CTX_init(&ctx);
17.
18. if(EVP_DecryptInit_ex(&ctx,EVP_get_cipherbynid(NID_aes_128_cbc),NULL,SessionKey,NULL))
19. {
20. int offseti=0;//in
21. int offseto=0;//out
22. int offsett=0;//temp
23. for(;;)
24. {
25. if(cbMsg-offseti<=MAX_ENCRYPT_LEN)
26. {
27. EVP_DecryptUpdate(&ctx, decryptMsg+offseto, &offsett, sMsg+offseti, cbMsg-offseti);
28. offseto+=offsett;
29. break;
30. }
31. else
32. {
33. EVP_DecryptUpdate(&ctx, decryptMsg+offseto, &offsett, sMsg+offseti, MAX_ENCRYPT_LEN);
34. offseti+=MAX_ENCRYPT_LEN;
35. offseto+=offsett;
36. }
37. }
38. EVP_DecryptFinal_ex(&ctx, decryptMsg+offseto, &offsett);
39.
40. offseto+=offsett;
41. cbEncryptMsg=offseto;
42. }
43. EVP_CIPHER_CTX_cleanup(&ctx);
44. std::cout << "openssl decrypt:" << std::endl;
45.
46.
47. char* ret = (char*)malloc(cbEncryptMsg + 1);
48. memcpy(ret,decryptMsg,cbEncryptMsg);
49. ret[cbEncryptMsg] = '\0';
50. std::cout << ret << endl;
51.}

测试下:

1.int _tmain(int argc, _TCHAR* argv[])
2.{
3. string text = "texttexttexttexttext";
4. unsigned char* key;
5. unsigned char* sEncryptMsg = (unsigned char*)malloc(text.size() + MD5_SIZE);
6. int len;
7. openssl_aes_encrypt(text,&key,sEncryptMsg,len);
8.
9.
10. unsigned char** sEncryptMsg_crypto = (unsigned char**)malloc(sizeof(unsigned char*));
11. int len_crypto;
12. cryptoAPI_encrypt(text,key,sEncryptMsg_crypto,len_crypto);
13. cout << "-----------------------------" << endl<<"cryptoAPI decrypt openssl"<<endl;
14. //cryptAPI_decrypt(sEncryptMsg,len,key);
15.
16. cout << "-----------------------------" << endl<<"cryptoAPI decrypt cryptoAPI"<<endl;
17. //cryptAPI_decrypt(*sEncryptMsg_crypto,len_crypto,key);
18.
19. cout << "-----------------------------" << endl<<"oepnssl decrypt openssl"<<endl;
20. //openssl_aes_decrypt(sEncryptMsg,len,key);
21.
22. cout << "-----------------------------" << endl<<"oepnssl decrypt cryptoAPI"<<endl;
23. //openssl_aes_decrypt(*sEncryptMsg_crypto,len_crypto,key);
24. return 0;
25.}

本篇文章来源于 Linux公社网站(www.linuxidc.com) 原文链接:http://www.linuxidc.com/Linux/2012-08/68723.htm

更多相关文章

  1. android google map v1 v2 v3 参考
  2. android apk 防止反编译技术第三篇-加密
  3. Android签名
  4. Android中Google Authenticator验证系统工作原理分析
  5. if快还是switch快?解密switch背后的秘密
  6. Android申请百度API时发布版SHA1改怎么填?
  7. android 加密 SQLCipher和Conceal
  8. android studio下百度地图sdk的初体验
  9. android apk 防止反编译技术第三篇-加密(转)

随机推荐

  1. Android 滑动侧边栏(Sliding Menu)第一种实
  2. android的四层体系结构,基于mvc三层结构浅
  3. Android图片转换
  4. Android实现用代码简单安装和卸载APK的方
  5. Android 事件冒泡及捕获
  6. android:imeOptions指定了弹出键盘时右下
  7. Android设置一个按钮右对齐
  8. Android应用商店——Splash页面的实现,And
  9. android java代码的启动:app_process
  10. cocos2dx在Eclipse下编译报错:Cannot fin