繼上次只有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,來個簡單點的版本。
- void cryptoAPI_encrypt(string text,unsigned char* pwd,unsigned char** encryptText,int &out_len)
- {
- HCRYPTPROV hCryptProv = NULL;
- HCRYPTKEY hKey = 0;
- HCRYPTHASH hHash = 0;
- int dwLength = 0;
-
- if(!CryptAcquireContext(&hCryptProv,
- NULL,
- CSP_NAME,//CSP_NAME
- PROV_RSA_AES,
- CRYPT_VERIFYCONTEXT))
- {
- DWORD dwLastErr = GetLastError();
-
- if(NTE_BAD_KEYSET == dwLastErr)
- {
- return;
- }
- else{
- if(!CryptAcquireContext(&hCryptProv,
- NULL,
- CSP_NAME,
- PROV_RSA_AES,
- CRYPT_NEWKEYSET))
- {
- return;
- }
- }
- }
- if(!CryptCreateHash(hCryptProv, CALG_MD5, 0, 0, &hHash))
- {
- return;
- }
-
- BYTE *pPwd = pwd;
-
- if(!CryptHashData(hHash, pPwd, 16, 0))
- {
- return;
- }
-
- if(!CryptDeriveKey(hCryptProv, CALG_AES_128, hHash, CRYPT_EXPORTABLE, &hKey))
- {
- return;
- }
-
- int len = text.length();
- BYTE *pData ;
- pData = (BYTE*)malloc(len*4);
- memcpy(pData,text.c_str(),len);
- DWORD dwLen = len;
-
- if(!CryptEncrypt(hKey, NULL, true, 0, pData, &dwLen, len*4))
- {
- return;
- }
-
- cout <<"--------------------------" << endl << "cryptoAPI encrypt"<<endl;
- printBytes(pData,dwLen);
- *encryptText = pData;
- out_len = dwLen;
- CryptDestroyHash(hHash);
- CryptDestroyKey(hKey);
- CryptReleaseContext(hCryptProv,0);
- }
這裡將傳進來的字節數組密鑰先進行MD5摘要後,再通過CryptoDeriveKey來得到最後用來加密的密鑰
openssl要以同樣的方式做一次這個步驟,首先是MD5摘要,相對比較簡單
- unsigned char* openssl_md5(unsigned char*sessionKey,size_t n)
- {
- unsigned char *ret = (unsigned char*)malloc(MD5_DIGEST_LENGTH);
- MD5(sessionKey,n,ret);
- return ret;
- }
然後再來實現CryptoDeriveKey,先來看下
MSDN上對於這個函數的說明
主要看remarks裡面的實現步驟:
- 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:
-
- 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.
- 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.
- 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.
- 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.
- 5.Concatenate the result of step 3 with the result of step 4.
- 6.Use the first n bytes of the result of step 5 as the derived key.
非常簡單的英文,不做翻譯了...
直接上openssl代碼實現
- //參見 http://msdn.microsoft.com/en-us/library/aa379916(v=vs.85).aspx remarks步驟
- unsigned char* derivedKey(unsigned char*sessionKey/*hash後的值*/,size_t n/*密鑰長度*/)
- {
- /**step 1*/
- unsigned char* buffer = (unsigned char*)malloc(64);
-
- for(int i = 0 ; i < 64;i++)
- {
- buffer[i] = 0x36;
- }
-
- int k = n;
-
- for(int i = 0 ; i < k ; i++)
- {
- buffer[i] = buffer[i] ^ sessionKey[i];
- }
-
- /*step 2*/
- unsigned char* buffer2 = (unsigned char*)malloc(64);
-
- for(int i = 0 ; i < 64;i++)
- {
- buffer2[i] = 0x5C;
- }
-
- for(int i = 0 ; i < k ; i++)
- {
- buffer2[i] = buffer2[i] ^ sessionKey[i];
- }
- /*step 3*/
- unsigned char* ret1 = openssl_md5(buffer,64);
- /*step 4*/
- unsigned char* ret2 = openssl_md5(buffer2,64);
-
-
- unsigned char* ret = (unsigned char*)malloc(128);
- for(int i = 0 ; i < 128;i++)
- {
- if(i<64)
- ret[i] = ret1[i];
- else
- ret[i] = ret2[i-64];
- }
- return ret;
- }
最麻煩的地方解決了...剩下再按照CryptoAPI的實現順序實現吧
- void openssl_aes_encrypt(string text,unsigned char** SessionKey_out/*這裡主要用作將產生的對稱密鑰輸出*/,unsigned char* sEncryptMsg,int &len)
- {
- OpenSSL_add_all_algorithms();
- //產生會話密鑰
- *SessionKey_out = (unsigned char*)malloc(MD5_SIZE);
- RAND_bytes(*SessionKey_out,MD5_SIZE);//產生隨機密鑰,輸出之後可以給其他方法是用了
- unsigned char* SessionKey = openssl_md5(*SessionKey_out,MD5_SIZE);
-
- SessionKey = derivedKey(SessionKey,MD5_SIZE);
-
- const unsigned char* sMsg = (const unsigned char*)text.c_str();
- int cbMsg = text.length();
- int cbEncryptMsg;
- //加密
- EVP_CIPHER_CTX ctx;
- EVP_CIPHER_CTX_init(&ctx);
- if(EVP_EncryptInit_ex(&ctx,EVP_get_cipherbynid(NID_aes_128_cbc),NULL,SessionKey,NULL))
- {
- int offseti=0;//in
- int offseto=0;//out
- int offsett=0;//temp
- for(;;)
- {
- if(cbMsg-offseti<=MAX_ENCRYPT_LEN)
- {
- EVP_EncryptUpdate(&ctx, sEncryptMsg+offseto, &offsett, sMsg+offseti, cbMsg-offseti);
- offseto+=offsett;
- break;
- }
- else
- {
- EVP_EncryptUpdate(&ctx, sEncryptMsg+offseto, &offsett, sMsg+offseti, MAX_ENCRYPT_LEN);
- offseti+=MAX_ENCRYPT_LEN;
- offseto+=offsett;
- }
- }
- EVP_EncryptFinal_ex(&ctx, sEncryptMsg+offseto, &offsett);
- offseto+=offsett;
- cbEncryptMsg=offseto;
- }
- EVP_CIPHER_CTX_cleanup(&ctx);
- std::cout << "openssl encrypt:" << std::endl;
- printBytes(sEncryptMsg,cbEncryptMsg);
- len = cbEncryptMsg;
- }
加密的搞定了,可以嘗試下了,同樣的密鑰和同樣的明文,密文輸出結果是一樣的就對了
下面是CrytpoAPI的解密:
- void cryptAPI_decrypt(unsigned char* text,int len,unsigned char* pwd)
- {
- HCRYPTPROV hCryptProv = NULL;
- HCRYPTKEY hKey = 0;
- HCRYPTHASH hHash = 0;
- int dwLength = 0;
-
- if(!CryptAcquireContext(&hCryptProv,
- NULL,
- CSP_NAME,//CSP_NAME
- PROV_RSA_AES,
- CRYPT_VERIFYCONTEXT))
- {
- DWORD dwLastErr = GetLastError();
-
- if(NTE_BAD_KEYSET == dwLastErr)
- {
- return;
- }
- else{
- if(!CryptAcquireContext(&hCryptProv,
- NULL,
- CSP_NAME,
- PROV_RSA_AES,
- CRYPT_NEWKEYSET))
- {
- return;
- }
- }
- }
-
-
- if(!CryptCreateHash(hCryptProv, CALG_MD5, 0, 0, &hHash))
- {
- return;
- }
-
- BYTE *pPwd = pwd;
-
- if(!CryptHashData(hHash, pPwd, 16, 0))
- {
- return;
- }
-
- if(!CryptDeriveKey(hCryptProv, CALG_AES_128, hHash, CRYPT_EXPORTABLE, &hKey))
- {
- return;
- }
-
- BYTE *pData = text;
- DWORD dwLen = len;
-
- if(!CryptDecrypt(hKey, NULL, true, 0, pData, &dwLen))
- {
- return;
- }
-
- cout <<"--------------------------" << endl << "cryptoAPI decrypt"<<endl;
- char* plainText = (char*)malloc(dwLen + 1);
- memcpy(plainText,pData,dwLen);
- plainText[dwLen] = '\0';
- cout << plainText << endl;
- CryptDestroyHash(hHash);
- CryptDestroyKey(hKey);
- CryptReleaseContext(hCryptProv,0);
- }
嘗試用這個方法解密CryptoAPI的加密和openssl的加密吧,都能輸出明文的
再來最後一個,openssl的解密
- void openssl_aes_decrypt(unsigned char* text,int len,unsigned char* SessionKeyP)
- {
- unsigned char* decryptMsg = (unsigned char*)malloc(len);
-
- OpenSSL_add_all_algorithms();
-
- unsigned char* SessionKey = openssl_md5(SessionKeyP,MD5_SIZE);
-
- SessionKey = derivedKey(SessionKey,MD5_SIZE);
-
- const unsigned char* sMsg = text;
- int cbMsg = len;
- int cbEncryptMsg;
- //解密
- EVP_CIPHER_CTX ctx;
- EVP_CIPHER_CTX_init(&ctx);
-
- if(EVP_DecryptInit_ex(&ctx,EVP_get_cipherbynid(NID_aes_128_cbc),NULL,SessionKey,NULL))
- {
- int offseti=0;//in
- int offseto=0;//out
- int offsett=0;//temp
- for(;;)
- {
- if(cbMsg-offseti<=MAX_ENCRYPT_LEN)
- {
- EVP_DecryptUpdate(&ctx, decryptMsg+offseto, &offsett, sMsg+offseti, cbMsg-offseti);
- offseto+=offsett;
- break;
- }
- else
- {
- EVP_DecryptUpdate(&ctx, decryptMsg+offseto, &offsett, sMsg+offseti, MAX_ENCRYPT_LEN);
- offseti+=MAX_ENCRYPT_LEN;
- offseto+=offsett;
- }
- }
- EVP_DecryptFinal_ex(&ctx, decryptMsg+offseto, &offsett);
-
- offseto+=offsett;
- cbEncryptMsg=offseto;
- }
- EVP_CIPHER_CTX_cleanup(&ctx);
- std::cout << "openssl decrypt:" << std::endl;
-
-
- char* ret = (char*)malloc(cbEncryptMsg + 1);
- memcpy(ret,decryptMsg,cbEncryptMsg);
- ret[cbEncryptMsg] = '\0';
- std::cout << ret << endl;
- }
測試下:
- int _tmain(int argc, _TCHAR* argv[])
- {
- string text = "texttexttexttexttext";
- unsigned char* key;
- unsigned char* sEncryptMsg = (unsigned char*)malloc(text.size() + MD5_SIZE);
- int len;
- openssl_aes_encrypt(text,&key,sEncryptMsg,len);
-
-
- unsigned char** sEncryptMsg_crypto = (unsigned char**)malloc(sizeof(unsigned char*));
- int len_crypto;
- cryptoAPI_encrypt(text,key,sEncryptMsg_crypto,len_crypto);
- cout << "-----------------------------" << endl<<"cryptoAPI decrypt openssl"<<endl;
- //cryptAPI_decrypt(sEncryptMsg,len,key);
-
- cout << "-----------------------------" << endl<<"cryptoAPI decrypt cryptoAPI"<<endl;
- //cryptAPI_decrypt(*sEncryptMsg_crypto,len_crypto,key);
-
- cout << "-----------------------------" << endl<<"oepnssl decrypt openssl"<<endl;
- //openssl_aes_decrypt(sEncryptMsg,len,key);
-
- cout << "-----------------------------" << endl<<"oepnssl decrypt cryptoAPI"<<endl;
- //openssl_aes_decrypt(*sEncryptMsg_crypto,len_crypto,key);
- return 0;
- }