openssl与cryptoAPI交互AES加密解密

时间:2022-12-06 18:32:17

继上次只有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;
}