[Windows编程笔记]AES加解密
2021-09-25 10:21 rnss 阅读(1374) 评论(0) 编辑 收藏 举报
AES加密函数
// AES加密 BOOL AesEncrypt(BYTE *pPassword, DWORD dwPasswordLength, BYTE *pData, DWORD &dwDataLength, DWORD dwBufferLength) { BOOL bRet = TRUE; HCRYPTPROV hCryptProv = NULL; HCRYPTHASH hCryptHash = NULL; HCRYPTKEY hCryptKey = NULL; do { // 获取CSP句柄 bRet = ::CryptAcquireContext(&hCryptProv, NULL, NULL, PROV_RSA_AES, CRYPT_VERIFYCONTEXT); if (FALSE == bRet) { ShowError("CryptAcquireContext"); break; } // 创建HASH对象 bRet = ::CryptCreateHash(hCryptProv, CALG_MD5, NULL, 0, &hCryptHash); if (FALSE == bRet) { ShowError("CryptCreateHash"); break; } // 对密钥进行HASH计算 bRet = ::CryptHashData(hCryptHash, pPassword, dwPasswordLength, 0); if (FALSE == bRet) { ShowError("CryptHashData"); break; } // 使用HASH来生成密钥 bRet = ::CryptDeriveKey(hCryptProv, CALG_AES_128, hCryptHash, CRYPT_EXPORTABLE, &hCryptKey); if (FALSE == bRet) { ShowError("CryptDeriveKey"); break; } // 加密数据 bRet = ::CryptEncrypt(hCryptKey, NULL, TRUE, 0, pData, &dwDataLength, dwBufferLength); if (FALSE == bRet) { ShowError("CryptEncrypt"); break; } } while (FALSE); // 关闭释放 if (hCryptKey) { ::CryptDestroyKey(hCryptKey); } if (hCryptHash) { ::CryptDestroyHash(hCryptHash); } if (hCryptProv) { ::CryptReleaseContext(hCryptProv, 0); } return bRet; }
流程如下:
首先用CryptAcquireContext函数获取CSP句柄
然后用CryptCreateHash函数创建HASH对象,HASH算法设置为CALG_MD5
然后用CryptHashData函数计算用户传入数据的MD5值
然后使用CryptDeriveKey函数来派生密钥
// 用于派生密钥 BOOL CryptDeriveKey( HCRYPTPROV hProv, // CryptAcquireContext创建的CSP句柄 ALG_ID Algid, // 标识加密算法,CALG_AES_128表示128位AES对称加密算法 HCRYPTHASH hBaseData, // CryptCreateHash创建的HASH对象的句柄 DWORD dwFlags, // 指定生成密钥的类型,CRYPT_EXPORTABLE表示可以用CryptExportKey函数导出 HCRYPTKEY *phKey // 新生成密钥的句柄地址 );
然后用CryptEncrypt函数来加密数据
// 用来加密数据 BOOL CryptEncrypt( HCRYPTKEY hKey, // CryptDeriveKey函数生成的密钥 HCRYPTHASH hHash, // NULL BOOL Final, // TRUE DWORD dwFlags, // 0 BYTE *pbData, // 要加密的明文,该缓冲区中的纯文本会被密文覆盖 DWORD *pdwDataLen, // 入口处是要加密的明文长度,退出时是写入到pbData中的密文长度 DWORD dwBufLen // 指定输入pbData缓冲区的总大小,注意:密文长度可能大于明文长度 );
然后用CryptDestroyKey函数释放密钥句柄
然后用CryptDestroyHash函数释放HASH对象句柄
最后用CryptReleaseContext函数释放CSP句柄
AES解密函数
// AES解密 BOOL AesDecrypt(BYTE *pPassword, DWORD dwPasswordLength, BYTE *pData, DWORD &dwDataLength, DWORD dwBufferLength) { BOOL bRet = TRUE; HCRYPTPROV hCryptProv = NULL; HCRYPTHASH hCryptHash = NULL; HCRYPTKEY hCryptKey = NULL; do { // 获取CSP句柄 bRet = ::CryptAcquireContext(&hCryptProv, NULL, NULL, PROV_RSA_AES, CRYPT_VERIFYCONTEXT); if (FALSE == bRet) { ShowError("CryptAcquireContext"); break; } // 创建HASH对象 bRet = ::CryptCreateHash(hCryptProv, CALG_MD5, NULL, 0, &hCryptHash); if (FALSE == bRet) { ShowError("CryptCreateHash"); break; } // 对密钥进行HASH计算 bRet = ::CryptHashData(hCryptHash, pPassword, dwPasswordLength, 0); if (FALSE == bRet) { ShowError("CryptHashData"); break; } // 使用HASH来生成密钥 bRet = ::CryptDeriveKey(hCryptProv, CALG_AES_128, hCryptHash, CRYPT_EXPORTABLE, &hCryptKey); if (FALSE == bRet) { ShowError("CryptDeriveKey"); break; } // 解密数据 bRet = ::CryptDecrypt(hCryptKey, NULL, TRUE, 0, pData, &dwDataLength); if (FALSE == bRet) { ShowError("CryptDecrypt"); break; } } while (FALSE); // 关闭释放 if (hCryptKey) { ::CryptDestroyKey(hCryptKey); } if (hCryptHash) { ::CryptDestroyHash(hCryptHash); } if (hCryptProv) { ::CryptReleaseContext(hCryptProv, 0); } return bRet; }
流程如下,前四步与后三步与加密相同,只是将加密函数改为了解密函数:
首先用CryptAcquireContext函数获取CSP句柄
然后用CryptCreateHash函数创建HASH对象,HASH算法设置为CALG_MD5
然后用CryptHashData函数计算用户传入数据的MD5值
然后使用CryptDeriveKey函数来派生密钥
然后用CryptDecrypt函数来解密数据
// 用来解密数据 BOOL CryptDecrypt( HCRYPTKEY hKey, // 密钥句柄 HCRYPTHASH hHash, // NULL BOOL Final, // TRUE DWORD dwFlags, // 0 BYTE *pbData, // 要解密的数据,解密完成后,明文会放回到该缓冲区 DWORD *pdwDataLen // 调用此函数前,表示密文长度,返回时,表示明文长度 );
然后用CryptDestroyKey函数释放密钥句柄
然后用CryptDestroyHash函数释放HASH对象句柄
最后用CryptReleaseContext函数释放CSP句柄
完整代码
// CryptoApi_Aes_Test.cpp : 定义控制台应用程序的入口点。 // #include <stdio.h> #include <Windows.h> #include <tchar.h> void ShowError(char *pszText) { char szErr[MAX_PATH] = { 0 }; ::wsprintf(szErr, "%s Error[0x%x]\n", pszText, ::GetLastError()); #ifdef _DEBUG ::MessageBox(NULL, szErr, "ERROR", MB_OK | MB_ICONERROR); #endif } // AES加密 BOOL AesEncrypt(BYTE *pPassword, DWORD dwPasswordLength, BYTE *pData, DWORD &dwDataLength, DWORD dwBufferLength) { BOOL bRet = TRUE; HCRYPTPROV hCryptProv = NULL; HCRYPTHASH hCryptHash = NULL; HCRYPTKEY hCryptKey = NULL; do { // 获取CSP句柄 bRet = ::CryptAcquireContext(&hCryptProv, NULL, NULL, PROV_RSA_AES, CRYPT_VERIFYCONTEXT); if (FALSE == bRet) { ShowError("CryptAcquireContext"); break; } // 创建HASH对象 bRet = ::CryptCreateHash(hCryptProv, CALG_MD5, NULL, 0, &hCryptHash); if (FALSE == bRet) { ShowError("CryptCreateHash"); break; } // 对密钥进行HASH计算 bRet = ::CryptHashData(hCryptHash, pPassword, dwPasswordLength, 0); if (FALSE == bRet) { ShowError("CryptHashData"); break; } // 使用HASH来生成密钥 bRet = ::CryptDeriveKey(hCryptProv, CALG_AES_128, hCryptHash, CRYPT_EXPORTABLE, &hCryptKey); if (FALSE == bRet) { ShowError("CryptDeriveKey"); break; } // 加密数据 bRet = ::CryptEncrypt(hCryptKey, NULL, TRUE, 0, pData, &dwDataLength, dwBufferLength); if (FALSE == bRet) { ShowError("CryptEncrypt"); break; } } while (FALSE); // 关闭释放 if (hCryptKey) { ::CryptDestroyKey(hCryptKey); } if (hCryptHash) { ::CryptDestroyHash(hCryptHash); } if (hCryptProv) { ::CryptReleaseContext(hCryptProv, 0); } return bRet; } // AES解密 BOOL AesDecrypt(BYTE *pPassword, DWORD dwPasswordLength, BYTE *pData, DWORD &dwDataLength, DWORD dwBufferLength) { BOOL bRet = TRUE; HCRYPTPROV hCryptProv = NULL; HCRYPTHASH hCryptHash = NULL; HCRYPTKEY hCryptKey = NULL; do { // 获取CSP句柄 bRet = ::CryptAcquireContext(&hCryptProv, NULL, NULL, PROV_RSA_AES, CRYPT_VERIFYCONTEXT); if (FALSE == bRet) { ShowError("CryptAcquireContext"); break; } // 创建HASH对象 bRet = ::CryptCreateHash(hCryptProv, CALG_MD5, NULL, 0, &hCryptHash); if (FALSE == bRet) { ShowError("CryptCreateHash"); break; } // 对密钥进行HASH计算 bRet = ::CryptHashData(hCryptHash, pPassword, dwPasswordLength, 0); if (FALSE == bRet) { ShowError("CryptHashData"); break; } // 使用HASH来生成密钥 bRet = ::CryptDeriveKey(hCryptProv, CALG_AES_128, hCryptHash, CRYPT_EXPORTABLE, &hCryptKey); if (FALSE == bRet) { ShowError("CryptDeriveKey"); break; } // 解密数据 bRet = ::CryptDecrypt(hCryptKey, NULL, TRUE, 0, pData, &dwDataLength); if (FALSE == bRet) { ShowError("CryptDecrypt"); break; } } while (FALSE); // 关闭释放 if (hCryptKey) { ::CryptDestroyKey(hCryptKey); } if (hCryptHash) { ::CryptDestroyHash(hCryptHash); } if (hCryptProv) { ::CryptReleaseContext(hCryptProv, 0); } return bRet; } int _tmain(int argc, _TCHAR* argv[]) { BYTE pData[MAX_PATH] = { 0 }; DWORD dwDataLength = 0, dwBufferLength = MAX_PATH; DWORD i = 0; ::RtlZeroMemory(pData, dwBufferLength); ::lstrcpy((char *)pData, "What is your name? DemonGan"); dwDataLength = 1 + ::lstrlen((char *)pData); printf("Text[%d]\n", dwDataLength); for (i = 0; i < dwDataLength; i++) { printf("%x ", pData[i]); } printf("\n\n"); // AES 加密 AesEncrypt((BYTE *)"DemonGanDemonGan", 16, pData, dwDataLength, dwBufferLength); printf("AES Encrypt[%d]\n", dwDataLength); for (i = 0; i < dwDataLength; i++) { printf("%x ", pData[i]); } printf("\n\n"); // AES 解密 AesDecrypt((BYTE *)"DemonGanDemonGan", 16, pData, dwDataLength, dwBufferLength); printf("AES Decrypt[%d]\n", dwDataLength); for (i = 0; i < dwDataLength; i++) { printf("%x ", pData[i]); } printf("\n\n"); system("pause"); return 0; }
小结
在AES标准规范中,分组长度只能是128位,也就是说,每个分组为16个字节(每个字节8位)。密钥长度可以使用128位、192位或256位。因此,在本程序中,密钥为16字节(128位)。
任何程序在使用CryptoAPI函数来进行数据计算或是加/解密之前,都需要先调用CryptAcquireContext函数来获取加密服务提供程序所需的CSP句柄。
本节的程序并不直接使用明文密钥作为AES的加密密码,而是把明文密钥的MD5值作为基础密钥通过调用CryptDeriveKey函数来派生出AES的加密密钥。
完成派生密钥后,可以调用CryptEncrypt函数来根据派生密钥中指定的加密算法进行加密运算。将第三个参数Final置为TRUE,它表示该加密是AES加密数据中的最后一组数据,这样系统会自动按照分组长度对数据进行填充并计算。其中,一定要确保数据缓冲区足够大,能够满足加密数据的存放,否则程序会出错。
在获取解密密钥后,可以直接调用CryptDecrypt函数来对密文进行解密操作。由于在加密的时候,将第三个参数Final置为TRUE来加密数据,使用在解密的时候,也要对应把Final置为TRUE来解密密文。Final置为TRUE表示该数据是AES解密数据中的最后一组数据,这样系统会自动按照分组长度对数据进行填充并计算。