继上次只有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,来个简单点的版本

[cpp] view plain copy print ?
  1. voidcryptoAPI_encrypt(stringtext,unsignedchar*pwd,unsignedchar**encryptText,int&out_len)
  2. {
  3. HCRYPTPROVhCryptProv=NULL;
  4. HCRYPTKEYhKey=0;
  5. HCRYPTHASHhHash=0;
  6. intdwLength=0;
  7. if(!CryptAcquireContext(&hCryptProv,
  8. NULL,
  9. CSP_NAME,//CSP_NAME
  10. PROV_RSA_AES,
  11. CRYPT_VERIFYCONTEXT))
  12. {
  13. DWORDdwLastErr=GetLastError();
  14. if(NTE_BAD_KEYSET==dwLastErr)
  15. {
  16. return;
  17. }
  18. else{
  19. if(!CryptAcquireContext(&hCryptProv,
  20. NULL,
  21. CSP_NAME,
  22. PROV_RSA_AES,
  23. CRYPT_NEWKEYSET))
  24. {
  25. return;
  26. }
  27. }
  28. }
  29. if(!CryptCreateHash(hCryptProv,CALG_MD5,0,0,&hHash))
  30. {
  31. return;
  32. }
  33. BYTE*pPwd=pwd;
  34. if(!CryptHashData(hHash,pPwd,16,0))
  35. {
  36. return;
  37. }
  38. if(!CryptDeriveKey(hCryptProv,CALG_AES_128,hHash,CRYPT_EXPORTABLE,&hKey))
  39. {
  40. return;
  41. }
  42. intlen=text.length();
  43. BYTE*pData;
  44. pData=(BYTE*)malloc(len*4);
  45. memcpy(pData,text.c_str(),len);
  46. DWORDdwLen=len;
  47. if(!CryptEncrypt(hKey,NULL,true,0,pData,&dwLen,len*4))
  48. {
  49. return;
  50. }
  51. cout<<"--------------------------"<<endl<<"cryptoAPIencrypt"<<endl;
  52. printBytes(pData,dwLen);
  53. *encryptText=pData;
  54. out_len=dwLen;
  55. CryptDestroyHash(hHash);
  56. CryptDestroyKey(hKey);
  57. CryptReleaseContext(hCryptProv,0);
  58. }


这里将传进来的字节数组密钥先进行MD5摘要后,再通过CryptoDeriveKey来得到最后用来加密的密钥

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

[cpp] view plain copy print ?
  1. unsignedchar*openssl_md5(unsignedchar*sessionKey,size_tn)
  2. {
  3. unsignedchar*ret=(unsignedchar*)malloc(MD5_DIGEST_LENGTH);
  4. MD5(sessionKey,n,ret);
  5. returnret;
  6. }


然后再来实现CryptoDeriveKey,先来看下MSDN上对于这个函数的说明

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

[plain] view plain copy print ?
  1. Letnbetherequiredderivedkeylength,inbytes.ThederivedkeyisthefirstnbytesofthehashvalueafterthehashcomputationhasbeencompletedbyCryptDeriveKey.IfthehashisnotamemberoftheSHA-2familyandtherequiredkeyisforeither3DESorAES,thekeyisderivedasfollows:
  2. 1.Forma64-bytebufferbyrepeatingtheconstant0x3664times.LetkbethelengthofthehashvaluethatisrepresentedbytheinputparameterhBaseData.SetthefirstkbytesofthebuffertotheresultofanXORoperationofthefirstkbytesofthebufferwiththehashvaluethatisrepresentedbytheinputparameterhBaseData.
  3. 2.Forma64-bytebufferbyrepeatingtheconstant0x5C64times.SetthefirstkbytesofthebuffertotheresultofanXORoperationofthefirstkbytesofthebufferwiththehashvaluethatisrepresentedbytheinputparameterhBaseData.
  4. 3.Hashtheresultofstep1byusingthesamehashalgorithmasthatusedtocomputethehashvaluethatisrepresentedbythehBaseDataparameter.
  5. 4.Hashtheresultofstep2byusingthesamehashalgorithmasthatusedtocomputethehashvaluethatisrepresentedbythehBaseDataparameter.
  6. 5.Concatenatetheresultofstep3withtheresultofstep4.
  7. 6.Usethefirstnbytesoftheresultofstep5asthederivedkey.

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

直接上openssl代码实现

[cpp] view plain copy print ?
  1. //参见http://msdn.microsoft.com/en-us/library/aa379916(v=vs.85).aspxremarks步骤
  2. unsignedchar*derivedKey(unsignedchar*sessionKey/*hash后的值*/,size_tn/*密钥长度*/)
  3. {
  4. /**step1*/
  5. unsignedchar*buffer=(unsignedchar*)malloc(64);
  6. for(inti=0;i<64;i++)
  7. {
  8. buffer[i]=0x36;
  9. }
  10. intk=n;
  11. for(inti=0;i<k;i++)
  12. {
  13. buffer[i]=buffer[i]^sessionKey[i];
  14. }
  15. /*step2*/
  16. unsignedchar*buffer2=(unsignedchar*)malloc(64);
  17. for(inti=0;i<64;i++)
  18. {
  19. buffer2[i]=0x5C;
  20. }
  21. for(inti=0;i<k;i++)
  22. {
  23. buffer2[i]=buffer2[i]^sessionKey[i];
  24. }
  25. /*step3*/
  26. unsignedchar*ret1=openssl_md5(buffer,64);
  27. /*step4*/
  28. unsignedchar*ret2=openssl_md5(buffer2,64);
  29. unsignedchar*ret=(unsignedchar*)malloc(128);
  30. for(inti=0;i<128;i++)
  31. {
  32. if(i<64)
  33. ret[i]=ret1[i];
  34. else
  35. ret[i]=ret2[i-64];
  36. }
  37. returnret;
  38. }


最麻烦的地方解决了...剩下再按照CryptoAPI的实现顺序实现吧

[cpp] view plain copy print ?
  1. voidopenssl_aes_encrypt(stringtext,unsignedchar**SessionKey_out/*这里主要用作将产生的对称密钥输出*/,unsignedchar*sEncryptMsg,int&len)
  2. {
  3. OpenSSL_add_all_algorithms();
  4. //产生会话密钥
  5. *SessionKey_out=(unsignedchar*)malloc(MD5_SIZE);
  6. RAND_bytes(*SessionKey_out,MD5_SIZE);//产生随机密钥,输出之后可以给其他方法是用了
  7. unsignedchar*SessionKey=openssl_md5(*SessionKey_out,MD5_SIZE);
  8. SessionKey=derivedKey(SessionKey,MD5_SIZE);
  9. constunsignedchar*sMsg=(constunsignedchar*)text.c_str();
  10. intcbMsg=text.length();
  11. intcbEncryptMsg;
  12. //加密
  13. EVP_CIPHER_CTXctx;
  14. EVP_CIPHER_CTX_init(&ctx);
  15. if(EVP_EncryptInit_ex(&ctx,EVP_get_cipherbynid(NID_aes_128_cbc),NULL,SessionKey,NULL))
  16. {
  17. intoffseti=0;//in
  18. intoffseto=0;//out
  19. intoffsett=0;//temp
  20. for(;;)
  21. {
  22. if(cbMsg-offseti<=MAX_ENCRYPT_LEN)
  23. {
  24. EVP_EncryptUpdate(&ctx,sEncryptMsg+offseto,&offsett,sMsg+offseti,cbMsg-offseti);
  25. offseto+=offsett;
  26. break;
  27. }
  28. else
  29. {
  30. EVP_EncryptUpdate(&ctx,sEncryptMsg+offseto,&offsett,sMsg+offseti,MAX_ENCRYPT_LEN);
  31. offseti+=MAX_ENCRYPT_LEN;
  32. offseto+=offsett;
  33. }
  34. }
  35. EVP_EncryptFinal_ex(&ctx,sEncryptMsg+offseto,&offsett);
  36. offseto+=offsett;
  37. cbEncryptMsg=offseto;
  38. }
  39. EVP_CIPHER_CTX_cleanup(&ctx);
  40. std::cout<<"opensslencrypt:"<<std::endl;
  41. printBytes(sEncryptMsg,cbEncryptMsg);
  42. len=cbEncryptMsg;
  43. }


加密的搞定了,可以尝试下了,同样的密钥和同样的明文,密文输出结果是一样的就对了

下面是CrytpoAPI的解密:

[cpp] view plain copy print ?
  1. voidcryptAPI_decrypt(unsignedchar*text,intlen,unsignedchar*pwd)
  2. {
  3. HCRYPTPROVhCryptProv=NULL;
  4. HCRYPTKEYhKey=0;
  5. HCRYPTHASHhHash=0;
  6. intdwLength=0;
  7. if(!CryptAcquireContext(&hCryptProv,
  8. NULL,
  9. CSP_NAME,//CSP_NAME
  10. PROV_RSA_AES,
  11. CRYPT_VERIFYCONTEXT))
  12. {
  13. DWORDdwLastErr=GetLastError();
  14. if(NTE_BAD_KEYSET==dwLastErr)
  15. {
  16. return;
  17. }
  18. else{
  19. if(!CryptAcquireContext(&hCryptProv,
  20. NULL,
  21. CSP_NAME,
  22. PROV_RSA_AES,
  23. CRYPT_NEWKEYSET))
  24. {
  25. return;
  26. }
  27. }
  28. }
  29. if(!CryptCreateHash(hCryptProv,CALG_MD5,0,0,&hHash))
  30. {
  31. return;
  32. }
  33. BYTE*pPwd=pwd;
  34. if(!CryptHashData(hHash,pPwd,16,0))
  35. {
  36. return;
  37. }
  38. if(!CryptDeriveKey(hCryptProv,CALG_AES_128,hHash,CRYPT_EXPORTABLE,&hKey))
  39. {
  40. return;
  41. }
  42. BYTE*pData=text;
  43. DWORDdwLen=len;
  44. if(!CryptDecrypt(hKey,NULL,true,0,pData,&dwLen))
  45. {
  46. return;
  47. }
  48. cout<<"--------------------------"<<endl<<"cryptoAPIdecrypt"<<endl;
  49. char*plainText=(char*)malloc(dwLen+1);
  50. memcpy(plainText,pData,dwLen);
  51. plainText[dwLen]='\0';
  52. cout<<plainText<<endl;
  53. CryptDestroyHash(hHash);
  54. CryptDestroyKey(hKey);
  55. CryptReleaseContext(hCryptProv,0);
  56. }


尝试用这个方法解密CryptoAPI的加密和openssl的加密吧,都能输出明文的

再来最后一个,openssl的解密

[cpp] view plain copy print ?
  1. voidopenssl_aes_decrypt(unsignedchar*text,intlen,unsignedchar*SessionKeyP)
  2. {
  3. unsignedchar*decryptMsg=(unsignedchar*)malloc(len);
  4. OpenSSL_add_all_algorithms();
  5. unsignedchar*SessionKey=openssl_md5(SessionKeyP,MD5_SIZE);
  6. SessionKey=derivedKey(SessionKey,MD5_SIZE);
  7. constunsignedchar*sMsg=text;
  8. intcbMsg=len;
  9. intcbEncryptMsg;
  10. //解密
  11. EVP_CIPHER_CTXctx;
  12. EVP_CIPHER_CTX_init(&ctx);
  13. if(EVP_DecryptInit_ex(&ctx,EVP_get_cipherbynid(NID_aes_128_cbc),NULL,SessionKey,NULL))
  14. {
  15. intoffseti=0;//in
  16. intoffseto=0;//out
  17. intoffsett=0;//temp
  18. for(;;)
  19. {
  20. if(cbMsg-offseti<=MAX_ENCRYPT_LEN)
  21. {
  22. EVP_DecryptUpdate(&ctx,decryptMsg+offseto,&offsett,sMsg+offseti,cbMsg-offseti);
  23. offseto+=offsett;
  24. break;
  25. }
  26. else
  27. {
  28. EVP_DecryptUpdate(&ctx,decryptMsg+offseto,&offsett,sMsg+offseti,MAX_ENCRYPT_LEN);
  29. offseti+=MAX_ENCRYPT_LEN;
  30. offseto+=offsett;
  31. }
  32. }
  33. EVP_DecryptFinal_ex(&ctx,decryptMsg+offseto,&offsett);
  34. offseto+=offsett;
  35. cbEncryptMsg=offseto;
  36. }
  37. EVP_CIPHER_CTX_cleanup(&ctx);
  38. std::cout<<"openssldecrypt:"<<std::endl;
  39. char*ret=(char*)malloc(cbEncryptMsg+1);
  40. memcpy(ret,decryptMsg,cbEncryptMsg);
  41. ret[cbEncryptMsg]='\0';
  42. std::cout<<ret<<endl;
  43. }

测试下:

[cpp] view plain copy print ?
    1. int_tmain(intargc,_TCHAR*argv[])
    2. {
    3. stringtext="texttexttexttexttext";
    4. unsignedchar*key;
    5. unsignedchar*sEncryptMsg=(unsignedchar*)malloc(text.size()+MD5_SIZE);
    6. intlen;
    7. openssl_aes_encrypt(text,&key,sEncryptMsg,len);
    8. unsignedchar**sEncryptMsg_crypto=(unsignedchar**)malloc(sizeof(unsignedchar*));
    9. intlen_crypto;
    10. cryptoAPI_encrypt(text,key,sEncryptMsg_crypto,len_crypto);
    11. cout<<"-----------------------------"<<endl<<"cryptoAPIdecryptopenssl"<<endl;
    12. //cryptAPI_decrypt(sEncryptMsg,len,key);
    13. cout<<"-----------------------------"<<endl<<"cryptoAPIdecryptcryptoAPI"<<endl;
    14. //cryptAPI_decrypt(*sEncryptMsg_crypto,len_crypto,key);
    15. cout<<"-----------------------------"<<endl<<"oepnssldecryptopenssl"<<endl;
    16. //openssl_aes_decrypt(sEncryptMsg,len,key);
    17. cout<<"-----------------------------"<<endl<<"oepnssldecryptcryptoAPI"<<endl;
    18. //openssl_aes_decrypt(*sEncryptMsg_crypto,len_crypto,key);
    19. return0;
    20. }

更多相关文章

  1. 2种方式进行Spinner数据的添加
  2. Android(安卓)Service两种启动方式详解(总结版)
  3. 几种判断应用(Android(安卓)App)前后台状态的方法
  4. 深度揭秘android摄像头的autoFocus-----循环自动聚焦的实现(Andro
  5. Android(安卓)的网络编程(3)-HttpURLConnection接口
  6. TCL 雏鹰飞翔计划 Android篇
  7. Android开发中怎样调用系统Email发送邮件(多种调用方式)
  8. Android与Windows Socket通信,TLS双向认证
  9. Android获取控件宽高的几种方式

随机推荐

  1. Android(安卓)ViewPager实现左右滑动翻页
  2. Android(安卓)代码监控apk安装,卸载,替换
  3. Android(安卓)创建没有标题栏的对话框
  4. android 写入data/data/包名/file/中
  5. Android配置gradle 阿里云镜像
  6. Navicat连接MySQL时报10060、1045错误及m
  7. MySQL中SQL Mode的查看与设置详解
  8. MySQL数据库对敏感数据加密及解密的实现
  9. MySQL主从配置学习笔记
  10. mysql全量备份和快速恢复的方法整理