You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

AES CBC模式加解密结果不符问题求助

AES-CBC加解密结果不匹配的问题分析与解决

问题根源

你遇到的核心问题是数组越界写入,以及对AES加密后二进制数据的处理误区,我们一步步拆解:


1. 数组越界导致内存破坏

先看你的缓冲区定义:

unsigned char input_data[]= "Sandeep"; // sizeof(input_data) = 8(7个字符+末尾'\0')
unsigned char enc_buffer[sizeof(input_data)+1]; // 大小为9(索引范围0~8)
unsigned char dec_buffer[sizeof(input_data)+1]; // 同样大小为9

而你写的终止符赋值代码:

enc_buffer[sizeof(input_data)+1]='\0'; // sizeof(input_data)=8,索引为9,超出enc_buffer最大索引8
dec_buffer[sizeof(input_data)+1]='\0'; // 同样存在越界问题

这种越界写入会破坏内存中的其他数据(比如后续初始化的iv或密钥结构),直接导致解密过程出错,输出乱码。


2. 加密后密文的输出误区

AES加密后的结果是二进制数据,包含大量不可打印字符(甚至可能包含'\0'),不能直接用cout当作字符串输出:

  • 保留终止符时:越界写入破坏了内存,解密流程被干扰,输出乱码
  • 注释终止符后:解密过程正常执行,得到了正确的明文,但enc_buffer没有字符串终止符,cout会一直读取内存直到遇到第一个'\0',刚好后续内存区域是input_data,所以会附带输出原数据。

修复方案

方案一:正确处理字符串明文的加解密

如果你的明文是字符串,建议只加密有效字符(去掉末尾的'\0'),同时注意AES块加密的填充要求:

unsigned char aes_key[]= "asytfuhcilejnco";
unsigned char input_data[]= "Sandeep";
const int plaintext_len = strlen(input_data);
// AES-128块大小为16字节,加密缓冲区需至少为块大小
unsigned char enc_buffer[AES_BLOCK_SIZE];
// 解密缓冲区需容纳明文+终止符
unsigned char dec_buffer[plaintext_len + 1];
AES_KEY enc_key, dec_key;
unsigned char iv[AES_BLOCK_SIZE];

int main() {
    memset(iv, 0x00, AES_BLOCK_SIZE);
    AES_set_encrypt_key(aes_key, sizeof(aes_key)*8, &enc_key);
    
    // 对明文进行PKCS#7填充(补全到16字节块大小)
    unsigned char padded_input[AES_BLOCK_SIZE] = {0};
    memcpy(padded_input, input_data, plaintext_len);
    padded_input[plaintext_len] = AES_BLOCK_SIZE - plaintext_len;
    for(int i = plaintext_len + 1; i < AES_BLOCK_SIZE; i++){
        padded_input[i] = AES_BLOCK_SIZE - plaintext_len;
    }

    AES_cbc_encrypt(padded_input, enc_buffer, AES_BLOCK_SIZE, &enc_key, iv, AES_ENCRYPT);

    memset(iv, 0x00, AES_BLOCK_SIZE); // 解密必须使用和加密完全相同的IV
    AES_set_decrypt_key(aes_key, sizeof(aes_key)*8, &dec_key);
    AES_cbc_encrypt(enc_buffer, dec_buffer, AES_BLOCK_SIZE, &dec_key, iv, AES_DECRYPT);
    
    // 移除填充并添加字符串终止符
    int padding_len = dec_buffer[plaintext_len];
    dec_buffer[plaintext_len] = '\0';

    cout<<"input_data="<<input_data<<endl;
    // 密文用十六进制输出,避免不可打印字符乱码
    cout<<"enc_buffer (hex): ";
    for(int i=0; i<AES_BLOCK_SIZE; i++){
        printf("%02x", enc_buffer[i]);
    }
    cout<<endl;
    cout<<"dec_buffer="<<dec_buffer<<endl;
}

关键注意点:

  • AES块大小要求:AES-128的块大小是16字节,加密数据长度必须是块大小的整数倍,因此需要对明文进行填充(比如PKCS#7填充),否则OpenSSL的AES_cbc_encrypt只会处理完整的块,剩余字节会被忽略。
  • IV的复用:解密时必须使用和加密完全相同的IV,否则解密结果会完全错误。
  • 二进制数据输出:密文是二进制格式,建议用十六进制打印才能看到真实内容,不要直接当作字符串输出。

为什么注释终止符后解密正确?

注释掉越界的终止符赋值后,内存没有被破坏,解密流程正常执行,得到了包含原明文'\0'的正确结果,所以cout能正确显示原数据。而enc_buffer因为没有终止符,cout会读取到后续内存中的input_data内容,因此看起来附带了原输入。

内容的提问来源于stack exchange,提问作者hari

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.28 09:53:33