AES-256-GCM模式下硬编码密文解密失败求助
AES-256-GCM硬编码解密失败的问题排查
第一大坑:IV长度设置与实际传入字节数不匹配
你在加解密函数里都通过EVP_CTRL_GCM_SET_IVLEN强制把IV长度设为16字节,但实际传入的iv数组只有12个有效字节(末尾的0是多余的字符串终止符):
// 代码硬设IV长度为16字节 if(1 != EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_IVLEN, 16, NULL)) handleErrors(); // 实际传入的IV仅12个有效字节,末尾0属于画蛇添足 const unsigned char iv[] = { 0xfd, 0xac, 0xc9, 0x0f, 0x36, 0x02, 0xf2, 0xbb, 0x03, 0x9b, 0x05, 0x67, 0 };
这会导致OpenSSL读取超出iv数组范围的栈内存来凑够16字节,而栈内存是随机的,每次运行都不一样:
- 加密时实际使用的是16字节IV(前12字节固定,后4字节随机)
- 解密时同样读取16字节IV,后4字节的随机值几乎不可能和加密时一致,直接触发Tag验证失败,解密返回-1。
第二坑:Tag的写入方式存在未定义行为
在EncDecTest中,你用std::string tag(16, '0')初始化Tag,然后强制转换tag.c_str()传给encrypt函数写入Tag:
std::string tag(16, '0'); // ... k = encrypt(..., (unsigned char*)tag.c_str());
c_str()返回的是只读指针,强制转成可写指针修改属于未定义行为,大概率Tag根本没被正确写入到tag字符串中,你硬编码的tagHard十有八九不是真实的加密Tag。
修复步骤
1. 统一IV长度(二选一即可)
推荐方案:使用GCM标准12字节IV
删除加解密函数中设置IV长度的代码(GCM默认IV长度就是12字节),同时去掉iv数组末尾多余的0:// 删除这行代码 // if(1 != EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_IVLEN, 16, NULL)) handleErrors(); // 修改IV数组,移除末尾的0 const unsigned char iv[] = { 0xfd, 0xac, 0xc9, 0x0f, 0x36, 0x02, 0xf2, 0xbb, 0x03, 0x9b, 0x05, 0x67 };若坚持使用16字节IV
补全iv数组到16字节,比如末尾添加4个0:// 保留IV长度设置代码 if(1 != EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_IVLEN, 16, NULL)) handleErrors(); // 补全IV至16字节 const unsigned char iv[] = { 0xfd, 0xac, 0xc9, 0x0f, 0x36, 0x02, 0xf2, 0xbb, 0x03, 0x9b, 0x05, 0x67, 0x00, 0x00, 0x00, 0x00 };
2. 正确处理Tag存储,别再用c_str()
要么使用std::string的data()方法(C++11及以后支持,返回可写指针),要么直接用数组存储Tag:
// 方法1:使用std::string::data() std::string tag(16, '\0'); k = encrypt(..., (unsigned char*)tag.data()); // 方法2:直接用数组,更省心 unsigned char tag[16] = {0}; k = encrypt(..., tag);
3. 重新获取正确的密文和Tag
修复上述问题后,运行EncDecTest,通过十六进制打印输出真实的密文和Tag(可以写个小工具函数循环打印),再将正确的值硬编码到simpleDecryptionTest中。
额外提醒
- 二进制数据(IV、密文、Tag)不需要添加字符串终止符
0,它们是纯字节流,加了只会导致长度计算错误或读取多余字节。 - 打印二进制数据务必用十六进制,避免不可打印字符造成的误判,比如写个简单的打印函数:
void printHex(const unsigned char* data, int len) { for(int i=0; i<len; i++) { printf("%02X ", data[i]); } printf("\n"); }
内容的提问来源于stack exchange,提问作者Oleg
相关产品推荐
相关产品推荐

