EVP_DecryptUpdate()传32返回0传64返回1问题排查
问题表现
- 调用
EVP_DecryptUpdate()时,若传入参数声明输出缓冲区可用大小为32字节,函数返回0(执行失败);将声明的可用大小改为64字节时函数返回1(执行成功),但两种场景下输出参数cipher_len的值始终为0,实际定义的输出缓冲区大小为4096字节,不存在空间不足的问题。 - 根据OpenSSL官方文档说明,
EVP_DecryptUpdate()解密操作执行成功时返回值应为1。
复现代码如下:
#include <openssl/evp.h> #include <stdio.h> #include <string.h> void dump_head(unsigned char *buf, size_t len) { unsigned end, i; for (end = len; end > 0; end--) if (buf[end-1] != 0) break; printf("buf = {"); for (i = 0; i < end; i++) printf(" %02hhx,", buf[i]); printf(" }\n"); } int main(void) { unsigned char key[] = "0123456789abcdef"; unsigned char iv[] = "1234567887654321"; unsigned char indata[32] = "0123456789abcdeffedcba9876543210"; unsigned char buf[4096]; unsigned pos; int cipher_len; EVP_CIPHER_CTX *ctx; ctx = EVP_CIPHER_CTX_new(); EVP_CIPHER_CTX_init(ctx); EVP_CIPHER_CTX_set_padding(ctx, 0); EVP_CIPHER_CTX_set_flags(ctx, EVP_CIPHER_CTX_FLAG_WRAP_ALLOW); printf("decrypt init: %d\n", EVP_DecryptInit_ex(ctx, EVP_aes_256_wrap_pad(), NULL, key, iv)); printf("decrypt update: %d\n", EVP_DecryptUpdate(ctx, (unsigned char *)buf, &cipher_len, indata, 32)); printf("Got %d\n", cipher_len); dump_head(buf, sizeof(buf)); printf("Final!\n"); memset(buf, 0, sizeof(buf)); EVP_DecryptFinal_ex(ctx, buf, &cipher_len); printf("Got %d\n", cipher_len); dump_head(buf, sizeof(buf)); return 0; }
根因分析
不存在遗漏配置的参数,问题来自现有参数配置不符合所选算法的强制要求,共3处错误:
- 密钥长度与选用算法不匹配:选用的
EVP_aes_256_wrap_pad()是AES-256密钥封装算法,要求密钥长度必须为32字节,代码中定义的key有效长度仅16字节,不符合算法初始化要求。 - 错误关闭了填充配置:
EVP_aes_256_wrap_pad()本身是带填充的密钥封装算法,内置了填充处理逻辑,代码中调用EVP_CIPHER_CTX_set_padding(ctx, 0)强制关闭填充,会导致算法内部长度计算逻辑异常,无法正常输出解密后的明文、也无法更新输出长度参数,这就是cipher_len始终为0的直接原因。 - 传入的输出缓冲区长度不符合算法校验规则:AES密钥封装类算法(含带pad版本)在
EVP_DecryptUpdate调用时,强制要求声明的输出缓冲区可用长度至少为输入密文长度 + 8字节——该预留空间用于存放算法内部处理时的临时校验值,和最终输出的明文长度无关。代码中输入密文长度为32字节,要求声明的缓冲区长度至少为40字节,传入32时直接触发长度校验失败返回0;传入64时仅满足了长度校验要求,但因为填充配置错误,仍然无法正常输出结果。
另外代码中EVP_CIPHER_CTX_new()已经完成了上下文的初始化操作,后续调用EVP_CIPHER_CTX_init(ctx)属于冗余调用,虽然不会直接触发错误,但无实际意义。
修正方法
- 匹配密钥与算法:如果使用16字节长度的密钥,将算法替换为
EVP_aes_128_wrap_pad();如果要使用EVP_aes_256_wrap_pad(),则将密钥长度调整为32字节。 - 删除
EVP_CIPHER_CTX_set_padding(ctx, 0)这行代码,保留算法默认的填充配置。 - 调用
EVP_DecryptUpdate前,先给cipher_len赋值为输出缓冲区的实际可用大小(即4096,满足至少输入长度+8的要求即可),不需要额外缩小缓冲区。
内容的提问来源于stack exchange,提问作者Kivuos
相关产品推荐
相关产品推荐

