使用OpenSSL和C++实现AES-CBC加密时输出结果不一致的问题
问题分析与修复方案
核心问题1:十六进制字符串转字节数组错误
你的密钥和IV是十六进制字符串,但代码直接用std::copy把字符串的ASCII字符复制到字节数组里,这会导致实际使用的密钥/IV完全不是你预期的值。比如keyString里的字符"A"会被存为ASCII值0x41,而非十六进制对应的0xAC,这是加密结果乱变的主要原因。
修复方法:实现十六进制转字节函数
替换直接复制的代码,用专门的转换函数把十六进制字符串转成字节:
#include <cctype> // 十六进制字符串转字节数组 bool hexStrToBytes(const std::string& hexStr, uint8_t* outBytes, size_t outLen) { if (hexStr.length() != outLen * 2) return false; for (size_t i = 0; i < outLen; ++i) { char high = toupper(hexStr[i*2]); char low = toupper(hexStr[i*2+1]); uint8_t highByte = (high >= 'A') ? (high - 'A' + 10) : (high - '0'); uint8_t lowByte = (low >= 'A') ? (low - 'A' + 10) : (low - '0'); outBytes[i] = (highByte << 4) | lowByte; } return true; }
然后替换原代码中复制密钥和IV的部分:
// 替换原来的std::copy调用 std::uint8_t iv[AES_BLOCK_SIZE]; std::uint8_t key[AES_KEYLENGTH/8]; // 256位密钥对应32字节 if (!hexStrToBytes(keyString, key, AES_KEYLENGTH/8) || !hexStrToBytes(ivString, iv, AES_BLOCK_SIZE)) { // 处理转换错误,比如密钥/IV长度不匹配 return; }
核心问题2:未按标准填充明文
CBC模式要求输入长度必须是AES块大小(16字节)的整数倍。你虽然扩展了输出长度,但只是用vector默认的0填充,不符合PKCS#7标准填充规范——而且如果明文长度刚好是块大小的倍数,也需要额外填充一个完整块,否则解密时无法区分真实数据和填充数据。
修复方法:添加PKCS#7填充
在加密前对明文做填充:
// PKCS#7填充 size_t paddingLen = AES_BLOCK_SIZE - (inputLen % AES_BLOCK_SIZE); std::vector<uint8_t> paddedData(data.begin(), data.end()); paddedData.resize(inputLen + paddingLen, paddingLen); // 填充paddingLen个值为paddingLen的字节 size_t encLen = paddedData.size();
后续加密时用paddedData作为输入。
其他问题:二进制数据输出错误
你用printf("%s", encOut.data())打印加密结果是错的——加密后是二进制数据,可能包含空字节\0导致printf提前截断,而且二进制数据不可打印,应该用十六进制格式输出:
// 打印十六进制加密结果 printf("encrypted data: "); for (uint8_t b : encOut) { printf("%02X", b); } printf("\n");
修复后的完整代码片段
#define AES_KEYLENGTH 256 #include <cctype> bool hexStrToBytes(const std::string& hexStr, uint8_t* outBytes, size_t outLen) { if (hexStr.length() != outLen * 2) return false; for (size_t i = 0; i < outLen; ++i) { char high = toupper(hexStr[i*2]); char low = toupper(hexStr[i*2+1]); uint8_t highByte = (high >= 'A') ? (high - 'A' + 10) : (high - '0'); uint8_t lowByte = (low >= 'A') ? (low - 'A' + 10) : (low - '0'); outBytes[i] = (highByte << 4) | lowByte; } return true; } void NetworkHandler::SendEncryptedPacket(std::vector<std::uint8_t>& data) { std::string keyString = "AC3CF1B84D7C946640447DE9670E18BE8A45F49A286FC4D8404DD729491064E4"; std::string ivString = "5D4FB5A040DE76B316794BAC89FC3A48"; printf("noncrypted data: %s\n", data.data()); size_t inputLen = data.size(); // PKCS#7填充 size_t paddingLen = AES_BLOCK_SIZE - (inputLen % AES_BLOCK_SIZE); std::vector<uint8_t> paddedData(data.begin(), data.end()); paddedData.resize(inputLen + paddingLen, paddingLen); size_t encLen = paddedData.size(); std::vector<std::uint8_t> encOut(encLen); // 初始化密钥和IV std::uint8_t iv[AES_BLOCK_SIZE]; std::uint8_t key[AES_KEYLENGTH/8]; if (!hexStrToBytes(keyString, key, AES_KEYLENGTH/8) || !hexStrToBytes(ivString, iv, AES_BLOCK_SIZE)) { printf("Invalid key or IV\n"); return; } AES_KEY encKey; // 检查密钥是否有效(返回0为成功) if (AES_set_encrypt_key(key, AES_KEYLENGTH, &encKey) != 0) { printf("Failed to set encrypt key\n"); return; } // AES_cbc_encrypt会修改传入的iv,要保留原始IV需先复制 uint8_t ivCopy[AES_BLOCK_SIZE]; memcpy(ivCopy, iv, AES_BLOCK_SIZE); AES_cbc_encrypt(paddedData.data(), encOut.data(), encLen, &encKey, ivCopy, AES_ENCRYPT); // 打印十六进制加密结果 printf("encrypted data: "); for (uint8_t b : encOut) { printf("%02X", b); } printf("\n"); SendPacket(encOut); }
调试检查点
如果还要排查问题,可以重点看:
- 转换后的密钥/IV字节值是否和十六进制字符串对应(比如调试器看
key数组前几个字节是不是0xAC, 0x3C, 0xF1) - 填充后的明文长度是否是16的整数倍,填充字节是否正确
AES_set_encrypt_key的返回值(非0说明密钥无效)- 加密前后IV数组的变化(CBC会更新IV,需保留原始IV的话必须复制)
内容的提问来源于stack exchange,提问作者FoxMaccloud
相关产品推荐
相关产品推荐

