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

C语言循环拼接AES加解密unsigned char缓冲区异常问题

问题描述

我正在使用OpenSSL库实现AES加密功能。由于AES是分组密码,我需要将数据拆分为16字节大小的分块进行处理,若要还原完整消息,需要在程序处理完成后将所有分块拼接起来。
目前单个解密分块的输出符合预期,但拼接后的完整解密输出仅复制了第一个分块的内存内容;采用完全相同逻辑拼接的密文输出看似可以正常工作。

问题代码

#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <openssl/aes.h>

char key[] = "secretkey123";

int main() {
    unsigned char text_slice[128];
    unsigned char enc_slice[128];
    unsigned char dec_slice[128];
    
    unsigned char in[160];
    
    unsigned char enc_out[160] = "";
    unsigned char dec_out[160] = "";
    
    int i;
    int k = 10;
    for (i = 0; i < 16 * k; i++) {
        in[i] = 'A' + (rand() % 26);
    }
    in[160] = '\0';
    printf("IN:%s\n", in);
    
    AES_KEY enc_key, dec_key;
    
    AES_set_encrypt_key(key, 128, &enc_key);
    
    AES_set_decrypt_key(key, 128, &dec_key);
    
    for (i = 0; i < k; i++) {
        text_slice[0] = '\0';
        enc_slice[0] = "\0";
        dec_slice[0] = "\0";
        memcpy(&text_slice[0], &in[15 * i], 15);
        text_slice[16] = "\0";
        printf("TEXT SLICE: %s \n", text_slice);
        AES_encrypt(text_slice, enc_slice, &enc_key);
        memcpy(&enc_out[16 * i], &enc_slice[0], 16);
    }
    printf("ENC:%s\n", enc_out);
    for (i = 0; i < k; i++) {
        text_slice[0] = '\0';
        enc_slice[0] = "\0";
        dec_slice[0] = "\0";
        memcpy(enc_slice, &enc_out[16 * i], 16);
        enc_slice[16] = "\0";
    
        AES_decrypt(enc_slice, dec_slice, &dec_key);
        printf("Dec slice:%s \n", dec_slice);
        memcpy(&dec_out[16 * i], &dec_slice[0], 16);
    }
    printf("DEC OUT:%s\n", dec_out);
    
    return 0;
}

运行输出

IN:NWLRBBMQBHCDARZOWKKYHIDDQSCDXRJMOWFRXSJYBLDBEFSARCBYNECDYGGXXPKLORELLNMPAPQFWKHOPKMCOQHNWNKUEWHSQMGBBUQCLJJIVSWMDKQTBXIXMVTRRBLJPTNSNFWZQFJMAFADRRWSOFSBCNUVQHFF
TEXT SLICE: NWLRBBMQBHCDARZ 
TEXT SLICE: OWKKYHIDDQSCDXR 
TEXT SLICE: JMOWFRXSJYBLDBE 
TEXT SLICE: FSARCBYNECDYGGX 
TEXT SLICE: XPKLORELLNMPAPQ 
TEXT SLICE: FWKHOPKMCOQHNWN 
TEXT SLICE: KUEWHSQMGBBUQCL 
TEXT SLICE: JJIVSWMDKQTBXIX 
TEXT SLICE: MVTRRBLJPTNSNFW 
TEXT SLICE: ZQFJMAFADRRWSOF 
ENC:j�Q���
𢨫�7֡���*n���R  ��m7�zI#4��=v�#�(��V7��ח9.R�q����:C�%��_��!q��(��l��j�3�1�h��
Dec slice:NWLRBBMQBHCDARZ 
Dec slice:OWKKYHIDDQSCDXR 
Dec slice:JMOWFRXSJYBLDBE 
Dec slice:FSARCBYNECDYGGX 
Dec slice:XPKLORELLNMPAPQ 
Dec slice:FWKHOPKMCOQHNWN 
Dec slice:KUEWHSQMGBBUQCL 
Dec slice:JJIVSWMDKQTBXIX 
Dec slice:MVTRRBLJPTNSNFW 
Dec slice:ZQFJMAFADRRWSOF 
DEC OUT:NWLRBBMQBHCDARZ

错误原因

代码存在多处未定义行为和逻辑错误,最终导致拼接结果异常:

  • 数组越界访问:in、enc_out、dec_out三个数组长度均为160字节,合法下标范围是0~159,代码中in[160] = '\0';属于越界写,会直接破坏栈上相邻变量的内存数据。
  • 分块逻辑错误:AES分组固定为16字节,分块读取明文时,块起始偏移应为16 * i,每次拷贝长度应为16字节。你写的memcpy(&text_slice[0], &in[15 * i], 15);使用15作为步长和拷贝长度,导致块之间错位重叠,每个块实际只读取了15字节有效数据,第16字节是栈上未初始化的垃圾值。单个分块打印看似正确只是内存布局的巧合,本质分块逻辑完全错误。
  • 结束符赋值类型错误:代码中多处使用xxx[pos] = "\0"的写法,双引号包裹的"\0"是字符串常量,本质是指针类型,将指针值截断赋值给unsigned char类型的元素,根本无法正确将对应位置设置为0值结束符。正确写法应为xxx[pos] = '\0'(使用单引号包裹字符)。
  • 密钥长度不匹配:你使用AES-128模式,要求密钥必须为16字节,但你定义的密钥"secretkey123"实际仅13字节(12个可见字符+自动添加的末尾'\0'),AES_set_encrypt_key会越界读取栈上的随机值作为密钥,直接导致加解密结果不可控。
  • 密文拼接正常是错觉:密文是二进制数据,本身可能包含任意字节值,用printf("%s")打印时遇到0值就会停止输出,你看到的乱码只是密文开头到第一个0值的片段,并非完整拼接的密文。解密拼接后只显示第一个块,是前述越界写、内存破坏问题导致dec_out第一个块之后的位置被意外写入0值,printf遇到0就终止输出,看起来就像只拼接了第一个块。

修复要点

  1. 调整数组长度避免越界,存储160字节数据+末尾结束符需要将数组长度设为161,或不要直接用字符串函数处理二进制数据。
  2. 将分块偏移和拷贝长度统一改为16,保证块对齐无重叠。
  3. 使用正确的字符字面量给数组置0结束符,不要混淆字符串和字符类型。
  4. 将密钥填充到16字节长度,满足AES-128的密钥长度要求。
  5. 二进制密文不要用%s直接打印,否则无法看到完整内容。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.03 09:03:50