使用OpenSSL AES-ECB解密时丢失最后一段明文的问题
OpenSSL AES-ECB解密丢失最后一段明文问题排查
问题描述
用OpenSSL C API实现AES-ECB加密解密时,解密后丢失最后一段明文(示例中所有'C'字符)。EVP_DecryptUpdate返回长度为32而非预期的48,EVP_DecryptFinal_ex调用失败,最终明文长度仅为32,怀疑是填充问题但无法定位原因。
复现代码
#include <stdio.h> #include <string.h> #include <openssl/conf.h> #include <openssl/evp.h> #include <openssl/err.h> int ecb_encrypt(unsigned char *plain_bytes, unsigned char *key, unsigned char *cipher_bytes, size_t plain_bytes_len, size_t *cipher_bytes_len) { EVP_CIPHER_CTX *ctx; int len = 0; if(!(ctx = EVP_CIPHER_CTX_new())) return 4; if(1 != EVP_EncryptInit_ex(ctx, EVP_aes_128_ecb(), NULL, key, NULL)) return 5; if(1 != EVP_EncryptUpdate(ctx, cipher_bytes, &len, plain_bytes, plain_bytes_len)) return 6; *cipher_bytes_len = len; if(1 != EVP_EncryptFinal_ex(ctx, cipher_bytes + *cipher_bytes_len, &len)) return 7; EVP_CIPHER_CTX_free(ctx); return 0; } int ecb_decrypt(unsigned char *cipher_bytes, unsigned char *key, unsigned char *plain_bytes, size_t cipher_bytes_len, size_t *plain_bytes_len) { EVP_CIPHER_CTX *ctx; int len = 0; if(!(ctx = EVP_CIPHER_CTX_new())) return 4; if(1 != EVP_DecryptInit_ex(ctx, EVP_aes_128_ecb(), NULL, key, NULL)) return 5; if(1 != EVP_DecryptUpdate(ctx, plain_bytes, &len, cipher_bytes, cipher_bytes_len)) return 6; *plain_bytes_len = len; printf("len = %d\n", len); printf("plain_bytes_len = %lu\n", *plain_bytes_len); if(1 != EVP_DecryptFinal_ex(ctx, plain_bytes + *plain_bytes_len, &len)) return 7; EVP_CIPHER_CTX_free(ctx); return 0; } int main() { unsigned char plain_text[] = "AAAAAAAAAAAAAAAABBBBBBBBBBBBBBBBCCCCCCCCCCCCCCCC"; unsigned char key[] = "ABCDEFGHIJKLMNOP"; unsigned char cipher_bytes[48]; size_t cipher_bytes_len = 0; ecb_encrypt(plain_text, key, (unsigned char *) &cipher_bytes, 48, &cipher_bytes_len); printf("cipher_bytes_len = %lu\n", cipher_bytes_len); //decrypt size_t plain_bytes_len = 0; unsigned char plain_bytes[cipher_bytes_len]; ecb_decrypt(cipher_bytes, key, (unsigned char *) &plain_bytes, cipher_bytes_len, &plain_bytes_len); printf("plain_bytes_len = %lu\n", plain_bytes_len); for(size_t i = 0; i < plain_bytes_len; i++){ printf("%c", plain_bytes[i]); } return 0; }
程序输出
cipher_bytes_len = 48 len = 32 plain_bytes_len = 32 plain_bytes_len = 32 AAAAAAAAAAAAAAAABBBBBBBBBBBBBBBB
问题根源与修复方案
1. 加密函数未累加EVP_EncryptFinal_ex的输出长度
AES-128块大小为16字节,当明文长度是块大小整数倍时(示例中48字节正好是3个块),EVP_EncryptFinal_ex会额外添加一个完整的PKCS#7填充块(16字节)。原代码未将这个填充块的长度加到最终密文长度中,导致密文长度仅为48字节(实际应为64字节)。
修复后的加密函数:
int ecb_encrypt(unsigned char *plain_bytes, unsigned char *key, unsigned char *cipher_bytes, size_t plain_bytes_len, size_t *cipher_bytes_len) { EVP_CIPHER_CTX *ctx; int len = 0; if(!(ctx = EVP_CIPHER_CTX_new())) return 4; if(1 != EVP_EncryptInit_ex(ctx, EVP_aes_128_ecb(), NULL, key, NULL)) return 5; if(1 != EVP_EncryptUpdate(ctx, cipher_bytes, &len, plain_bytes, plain_bytes_len)) return 6; *cipher_bytes_len = len; if(1 != EVP_EncryptFinal_ex(ctx, cipher_bytes + *cipher_bytes_len, &len)) return 7; *cipher_bytes_len += len; // 累加填充块长度 EVP_CIPHER_CTX_free(ctx); return 0; }
2. 解密函数未累加EVP_DecryptFinal_ex的输出长度
EVP_DecryptFinal_ex负责处理填充并返回去除填充后的明文长度,原代码未将该长度加到最终明文长度中。同时,由于加密后的密文长度错误,导致解密时无法正确解析填充,函数调用失败。
修复后的解密函数:
int ecb_decrypt(unsigned char *cipher_bytes, unsigned char *key, unsigned char *plain_bytes, size_t cipher_bytes_len, size_t *plain_bytes_len) { EVP_CIPHER_CTX *ctx; int len = 0; if(!(ctx = EVP_CIPHER_CTX_new())) return 4; if(1 != EVP_DecryptInit_ex(ctx, EVP_aes_128_ecb(), NULL, key, NULL)) return 5; if(1 != EVP_DecryptUpdate(ctx, plain_bytes, &len, cipher_bytes, cipher_bytes_len)) return 6; *plain_bytes_len = len; if(1 != EVP_DecryptFinal_ex(ctx, plain_bytes + *plain_bytes_len, &len)) return 7; *plain_bytes_len += len; // 累加Final阶段输出的明文长度 EVP_CIPHER_CTX_free(ctx); return 0; }
3. 主函数缓冲区大小不足
原代码中cipher_bytes仅定义为48字节,无法容纳加密后的64字节密文;解密时plain_bytes按错误的密文长度分配,缓冲区大小不足。
修复后的主函数:
int main() { unsigned char plain_text[] = "AAAAAAAAAAAAAAAABBBBBBBBBBBBBBBBCCCCCCCCCCCCCCCC"; unsigned char key[] = "ABCDEFGHIJKLMNOP"; unsigned char cipher_bytes[64]; // 扩容至64字节,适配加密后长度 size_t cipher_bytes_len = 0; ecb_encrypt(plain_text, key, cipher_bytes, 48, &cipher_bytes_len); printf("cipher_bytes_len = %lu\n", cipher_bytes_len); //decrypt size_t plain_bytes_len = 0; unsigned char plain_bytes[64]; // 分配足够大的缓冲区 ecb_decrypt(cipher_bytes, key, plain_bytes, cipher_bytes_len, &plain_bytes_len); printf("plain_bytes_len = %lu\n", plain_bytes_len); for(size_t i = 0; i < plain_bytes_len; i++){ printf("%c", plain_bytes[i]); } printf("\n"); return 0; }
补充说明
- OpenSSL默认使用PKCS#7填充:明文长度为块大小整数倍时,添加一个完整块(每个字节值为块大小0x10);解密时
EVP_DecryptFinal_ex自动去除填充。 - 调试时可调用
ERR_print_errors_fp(stderr)打印OpenSSL错误信息,快速定位问题。
内容的提问来源于stack exchange,提问作者DavidT
相关产品推荐
相关产品推荐

