歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
您现在的位置: Linux教程網 >> UnixLinux >  >> Linux編程 >> Linux編程

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. }  
Copyright © Linux教程網 All Rights Reserved