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

OpenSSL C API实现AES-128-CBC解密失败,求问题排查

解密失败(EVP_DecryptFinal返回0)的原因与修复方案

看起来你在把命令行的OpenSSL解密逻辑转成C API时,踩了几个常见的格式坑,这才导致EVP_DecryptFinal返回0(解密失败)。我帮你拆解一下问题,再给出修正后的代码:

问题1:直接把Base64字符串当作密文传入解密函数

命令行里的openssl enc -base64 -d会自动先对输入的Base64字符串解码,得到原始二进制密文,再执行解密。但你的代码里直接把Base64编码后的hex_enc_password传给了decrypt函数——而OpenSSL的EVP解密API只认二进制密文,不认Base64格式的文本。这就好比你把加密后的zip文件的Base64文本直接丢给解压软件,它肯定识别不了。

问题2:密钥格式完全错了

你的hex_key是hexlify(key)得到的十六进制字符串(比如把Base64密钥转成了类似"1ac4"的文本),但EVP_DecryptInit需要的是二进制密钥字节,不是十六进制字符串。举个例子:如果hexlify得到"1ac4",你需要把这个文本转换成两个字节0x1a和0xc4,而不是直接把"1ac4"这个字符串当密钥传入——这就像你把密码的ASCII文本当成密钥,而不是密码对应的二进制哈希值。

潜在问题:IV的长度与正确性

AES-128-CBC要求IV必须是16字节(和块大小一致),如果你的iv变量长度不对,或者和命令行里用的{iv}不一致,解密也会失败。


修正后的完整代码示例

我给你写了包含所有必要转换和错误处理的代码,你可以参考:

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

// 辅助函数:十六进制字符串转二进制字节
unsigned char* hex_to_bin(const char* hex_str, size_t* out_len) {
    size_t len = strlen(hex_str);
    if (len % 2 != 0) {
        *out_len = 0;
        return NULL;
    }
    *out_len = len / 2;
    unsigned char* bin_buf = malloc(*out_len);
    if (!bin_buf) {
        *out_len = 0;
        return NULL;
    }
    for (size_t i = 0; i < len; i += 2) {
        sscanf(hex_str + i, "%2hhx", &bin_buf[i/2]);
    }
    return bin_buf;
}

// 辅助函数:Base64解码(基于OpenSSL BIO)
unsigned char* base64_decode(const char* b64_str, size_t* out_len) {
    BIO *bio_mem, *bio_b64;
    size_t b64_len = strlen(b64_str);
    unsigned char* decrypted_buf = malloc(b64_len);
    if (!decrypted_buf) {
        *out_len = 0;
        return NULL;
    }
    bio_mem = BIO_new_mem_buf(b64_str, b64_len);
    bio_b64 = BIO_new(BIO_f_base64());
    bio_mem = BIO_push(bio_b64, bio_mem);
    BIO_set_flags(bio_mem, BIO_FLAGS_BASE64_NO_NL); // 忽略Base64中的换行符
    *out_len = BIO_read(bio_mem, decrypted_buf, b64_len);
    BIO_free_all(bio_mem);
    return decrypted_buf;
}

// 修正后的解密函数
char* decrypt(unsigned char *ciphertext, int ciphertext_len, unsigned char *key, unsigned char *iv ) {
    int out_len_update = 0;
    int out_len_final = 0;
    // 输出缓冲区要预留块大小的空间,处理PKCS#7 padding
    size_t out_buf_size = ciphertext_len + EVP_CIPHER_block_size(EVP_aes_128_cbc());
    unsigned char *plaintext_buf = malloc(out_buf_size);
    if (!plaintext_buf) {
        return NULL;
    }
    memset(plaintext_buf, 0, out_buf_size);
    
    EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new();
    if (!ctx) {
        free(plaintext_buf);
        return NULL;
    }
    
    // 初始化解密上下文,key和iv都是二进制字节
    if (EVP_DecryptInit_ex(ctx, EVP_aes_128_cbc(), NULL, key, iv) != 1) {
        EVP_CIPHER_CTX_free(ctx);
        free(plaintext_buf);
        return NULL;
    }
    
    // 解密主体数据
    if (EVP_DecryptUpdate(ctx, plaintext_buf, &out_len_update, ciphertext, ciphertext_len) != 1) {
        EVP_CIPHER_CTX_free(ctx);
        free(plaintext_buf);
        return NULL;
    }
    
    // 完成解密,处理padding
    if (EVP_DecryptFinal_ex(ctx, plaintext_buf + out_len_update, &out_len_final) != 1) {
        EVP_CIPHER_CTX_free(ctx);
        free(plaintext_buf);
        fprintf(stderr, "解密失败:可能是密文损坏、密钥/IV错误或padding问题\n");
        return NULL;
    }
    
    // 为文本明文添加结束符(如果明文是二进制数据可以去掉)
    plaintext_buf[out_len_update + out_len_final] = '\0';
    EVP_CIPHER_CTX_free(ctx);
    return (char*)plaintext_buf;
}

int main() {
    // 原始Base64格式的密钥
    char* key_b64 = "lbzA54tAAg3E/jPxiJc34e==";
    size_t key_bin_len;
    // 解码Base64得到二进制密钥字节
    unsigned char* key_bin = base64_decode(key_b64, &key_bin_len);
    if (!key_bin || key_bin_len != 16) { // AES-128需要16字节密钥
        fprintf(stderr, "无效密钥:长度必须为16字节\n");
        return 1;
    }
    
    // 原始二进制密文
    unsigned char encrypted[] = {0x3d,0xd2,0x02,0xc3,0xae,0x1e,0xf5,0x7e,0x33,0xc3,0x1d,0x7b,0x1e,0x48,0x73,0x84};
    size_t encrypted_len = sizeof(encrypted);
    
    // 模拟生成Base64编码后的密文(对应你的hex_enc_password)
    size_t b64_enc_len;
    unsigned char* hex_enc_password = base64_encode(encrypted, encrypted_len, &b64_enc_len); // 假设你有正确的base64_encode实现
    if (!hex_enc_password) {
        free(key_bin);
        return 1;
    }
    
    // 先解码Base64得到原始二进制密文
    size_t ciphertext_len;
    unsigned char* ciphertext = base64_decode((char*)hex_enc_password, &ciphertext_len);
    free(hex_enc_password);
    if (!ciphertext) {
        free(key_bin);
        return 1;
    }
    
    // 初始化IV,替换成你命令行里使用的真实IV(必须是16字节)
    unsigned char iv[16] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
                           0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f};
    
    // 执行解密
    char* plaintext = decrypt(ciphertext, ciphertext_len, key_bin, iv);
    free(ciphertext);
    free(key_bin);
    
    if (plaintext) {
        printf("解密结果:%s\n", plaintext);
        free(plaintext);
    }
    
    return 0;
}

关键修复点总结

  1. 添加Base64解码:解密前先把Base64编码的密文转成二进制,对应命令行的-base64 -d。
  2. 密钥格式转换:把Base64或十六进制格式的密钥转成二进制字节,这是EVP API要求的输入格式。
  3. 缓冲区预留空间:解密输出缓冲区要比密文长一个块大小,用来处理PKCS#7 padding。
  4. 完善错误处理:添加上下文创建、初始化的错误检查,避免内存泄漏。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.14 08:45:41