如何用WinApi(C/C++)生成兼容OpenSSL的AES-256-CBC安全随机密钥与IV
在C/C++中生成可用于OpenSSL的AES-256-CBC安全密钥与IV
核心需求
需要生成:
- 32字节的安全随机密钥(对应AES-256),转换为64字符的十六进制ASCII字符串,供OpenSSL的
-K参数使用 - 16字节的安全随机IV,转换为32字符的十六进制ASCII字符串,供OpenSSL的
-iv参数使用
生成的字符串需保存到文本文件,用于终端手动测试解密。同时需要替代CryptGenRandom的安全随机生成方案,解决原始随机字节无法直接作为OpenSSL输入的问题。
替代CryptGenRandom的安全随机生成方法
1. BCryptGenRandom(推荐,现代Windows API)
这是微软官方推荐的新一代密码学随机数生成接口,替代老旧的CryptGenRandom,支持Windows Vista及以上系统,安全性更高,属于CNG(Cryptography Next Generation)框架的标准组件。
2. CNG框架下的其他实现
本质与BCryptGenRandom同源,是现代Windows系统提供的标准安全随机数生成体系,避免了旧API的兼容性与安全性问题。
关键注意点
OpenSSL的-K和-iv参数要求输入十六进制编码的ASCII字符串,而非原始随机字节。因此生成流程应为:
- 生成指定长度的原始随机字节数组(32字节密钥/16字节IV)
- 将字节数组转换为十六进制ASCII字符串(每个字节对应两个十六进制字符)
- 将十六进制字符串保存到文件,供OpenSSL使用
- 加密时仍使用原始字节数组(而非十六进制字符串)传递给WinAPI的加密接口
修改后的完整代码示例
#include <windows.h> #include <bcrypt.h> #include <stdio.h> #include <string.h> #include <stdlib.h> #pragma comment(lib, "bcrypt.lib") #define DEFAULT_AES_KEY_SIZE 32 #define DEFAULT_IV_SIZE 16 #define BUFFER_FOR_PLAINTEXT 1024 // 字节数组转十六进制字符串 BOOL BytesToHexString(const BYTE* pBytes, DWORD dwLength, char** ppHexStr) { if (pBytes == NULL || dwLength == 0 || ppHexStr == NULL) return FALSE; // 每个字节占2个十六进制字符,加1存终止符 DWORD dwHexStrLen = dwLength * 2 + 1; *ppHexStr = (char*)calloc(dwHexStrLen, sizeof(char)); if (*ppHexStr == NULL) return FALSE; const char* szHexChars = "0123456789ABCDEF"; for (DWORD i = 0; i < dwLength; i++) { (*ppHexStr)[i * 2] = szHexChars[(pBytes[i] >> 4) & 0x0F]; (*ppHexStr)[i * 2 + 1] = szHexChars[pBytes[i] & 0x0F]; } return TRUE; } // 保存字符串到文件 BOOL SaveStringToFile(const char* szStr, const char* szFilePath) { FILE* pFile = fopen(szFilePath, "w"); if (pFile == NULL) return FALSE; fprintf(pFile, "%s", szStr); fclose(pFile); return TRUE; } int main() { HCRYPTPROV hProv = NULL; HCRYPTKEY hKey = NULL; BYTE* szKeyBytes = (BYTE*)calloc(DEFAULT_AES_KEY_SIZE, sizeof(BYTE)); BYTE* szIVBytes = (BYTE*)calloc(DEFAULT_IV_SIZE, sizeof(BYTE)); char* szKeyHex = NULL; char* szIVHex = NULL; char* ciphertext = 0; const char* plaintext = "测试明文内容"; // 示例明文 DWORD dwPlainSize = lstrlenA(plaintext), dwBufSize = 0; AES256KEYBLOB AESBlob; memset(&AESBlob, 0, sizeof(AESBlob)); NTSTATUS status; // -------------------------- // 1. 用BCryptGenRandom生成安全随机字节 // -------------------------- BCRYPT_ALG_HANDLE hAlg = NULL; // 打开随机数生成算法 status = BCryptOpenAlgorithmProvider(&hAlg, BCRYPT_RNG_ALGORITHM, NULL, 0); if (!BCRYPT_SUCCESS(status)) goto error; // 生成密钥字节 status = BCryptGenRandom(hAlg, szKeyBytes, DEFAULT_AES_KEY_SIZE, BCRYPT_USE_SYSTEM_PREFERRED_RNG); if (!BCRYPT_SUCCESS(status)) goto error; // 生成IV字节 status = BCryptGenRandom(hAlg, szIVBytes, DEFAULT_IV_SIZE, BCRYPT_USE_SYSTEM_PREFERRED_RNG); if (!BCRYPT_SUCCESS(status)) goto error; BCryptCloseAlgorithmProvider(hAlg, 0); // -------------------------- // 2. 转换为十六进制字符串并保存到文件 // -------------------------- if (!BytesToHexString(szKeyBytes, DEFAULT_AES_KEY_SIZE, &szKeyHex)) goto error; if (!BytesToHexString(szIVBytes, DEFAULT_IV_SIZE, &szIVHex)) goto error; printf("生成的密钥十六进制字符串:%s\n", szKeyHex); printf("生成的IV十六进制字符串:%s\n", szIVHex); // 保存到文件 if (!SaveStringToFile(szKeyHex, "aes_key.txt")) goto error; if (!SaveStringToFile(szIVHex, "aes_iv.txt")) goto error; // -------------------------- // 3. 执行AES-256-CBC加密(沿用原有逻辑,使用原始字节) // -------------------------- // 填充AES Blob结构 AESBlob.bhHdr.bType = PLAINTEXTKEYBLOB; AESBlob.bhHdr.bVersion = CUR_BLOB_VERSION; AESBlob.bhHdr.reserved = 0; AESBlob.bhHdr.aiKeyAlg = CALG_AES_256; AESBlob.dwKeySize = DEFAULT_AES_KEY_SIZE; memcpy(AESBlob.szBytes, szKeyBytes, DEFAULT_AES_KEY_SIZE); // 使用原始密钥字节 // 创建CSP if (!CryptAcquireContextA(&hProv, NULL, MS_ENH_RSA_AES_PROV_A, PROV_RSA_AES, CRYPT_VERIFYCONTEXT)) goto error; // 导入密钥 if (!CryptImportKey(hProv, (BYTE*)&AESBlob, sizeof(AES256KEYBLOB), NULL, CRYPT_EXPORTABLE, &hKey)) goto error; // 设置IV if (!CryptSetKeyParam(hKey, KP_IV, szIVBytes, 0)) // 使用原始IV字节 goto error; // 分配密文缓冲区 dwBufSize = BUFFER_FOR_PLAINTEXT + dwPlainSize; ciphertext = (char*)calloc(dwBufSize, sizeof(char)); if (ciphertext == NULL) goto error; memcpy_s(ciphertext, dwBufSize, plaintext, dwPlainSize); // 执行加密 if (!CryptEncrypt(hKey, NULL, TRUE, 0, (BYTE*)ciphertext, &dwPlainSize, dwBufSize)) goto error; printf("加密成功,密文长度:%u字节\n", dwPlainSize); // 清理资源 error: if (szKeyBytes) free(szKeyBytes); if (szIVBytes) free(szIVBytes); if (szKeyHex) free(szKeyHex); if (szIVHex) free(szIVHex); if (ciphertext) free(ciphertext); if (hKey) CryptDestroyKey(hKey); if (hProv) CryptReleaseContext(hProv, 0); return 0; }
OpenSSL解密命令示例
假设加密后的文件为encrypted.bin,使用生成的密钥和IV文件解密:
# 从文件读取密钥和IV,执行解密 openssl aes-256-cbc -d -K $(cat aes_key.txt) -iv $(cat aes_iv.txt) -in encrypted.bin -out decrypted.txt
内容的提问来源于stack exchange,提问作者rmroot
相关产品推荐
相关产品推荐

