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就终止输出,看起来就像只拼接了第一个块。
修复要点
- 调整数组长度避免越界,存储160字节数据+末尾结束符需要将数组长度设为161,或不要直接用字符串函数处理二进制数据。
- 将分块偏移和拷贝长度统一改为16,保证块对齐无重叠。
- 使用正确的字符字面量给数组置0结束符,不要混淆字符串和字符类型。
- 将密钥填充到16字节长度,满足AES-128的密钥长度要求。
- 二进制密文不要用
%s直接打印,否则无法看到完整内容。
内容的提问来源于stack exchange,提问作者Per Mertesacker
相关产品推荐
相关产品推荐

