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

使用OpenSSL无法解密C代码加密文件的问题求助

问题:OpenSSL EVP Seal加密后用rsautl解密失败(填充错误)

背景信息

通过以下OpenSSL命令生成RSA密钥对:

openssl genrsa -out key.pem
openssl rsa -in key.pem -out key.pub -pubout

使用以下C代码将test.txt加密为test.enc:

#include <stdio.h>
#include <stdlib.h>

#include <limits.h>
#include <dirent.h>

#include <openssl/bio.h>
#include <openssl/evp.h>
#include <openssl/pem.h>

#define BUFFER_SIZE 1024

static int evp_aes_encrypt(char *in_path, char *out_path, EVP_PKEY *pkey)
{
    FILE *in_file = fopen(in_path, "rb");
    if (!in_file)
        return -1;

    FILE *out_file = fopen(out_path, "wb");
    if (!out_file)
    {
        fclose(in_file);
        return -1;
    }

    EVP_CIPHER_CTX* ctx = EVP_CIPHER_CTX_new();
    if (!ctx)
    {
        fclose(in_file);
        fclose(out_file);
        return -1;
    }

    int len;
    unsigned char iv[EVP_MAX_IV_LENGTH];
    int i;
    unsigned char* ek = NULL;

    if (EVP_SealInit(ctx, EVP_aes_256_cbc(), &ek, &len, iv, &pkey, 1) != 1)
    {
        printf("1\n");
        EVP_CIPHER_CTX_free(ctx);
        fclose(in_file);
        fclose(out_file);
        return -1;
    }

    unsigned char in_buffer[BUFFER_SIZE];
    unsigned char out_buffer[BUFFER_SIZE + EVP_MAX_IV_LENGTH];

    int bytes_read, bytes_written;
    while ((bytes_read = fread(in_buffer, 1, BUFFER_SIZE, in_file)) > 0)
    {
        if (EVP_SealUpdate(ctx, out_buffer, &bytes_written, in_buffer, bytes_read) != 1) {
            EVP_CIPHER_CTX_free(ctx);
            fclose(in_file);
            fclose(out_file);
            return -1;
        }
        fwrite(out_buffer, 1, bytes_written, out_file);
    }

    if (EVP_SealFinal(ctx, out_buffer, &bytes_written) != 1) {
        EVP_CIPHER_CTX_free(ctx);
        fclose(in_file);
        fclose(out_file);
        return -1;
    }

    fwrite(out_buffer, 1, bytes_written, out_file);

    EVP_CIPHER_CTX_free(ctx);
    fclose(in_file);
    fclose(out_file);

    return 0;
}

int main(void)
{
    FILE *pub = fopen("key.pub", "rb");
    EVP_PKEY *pkey = PEM_read_PUBKEY(pub, NULL, NULL, NULL);
    evp_aes_encrypt("test.txt", "test.enc", pkey);
    return 0;
}

尝试用以下OpenSSL命令解密时出现错误:

openssl rsautl -in test.enc -out test.dec -inkey key.pem -decrypt

错误信息:

RSA operation error
407D290301000000:error:0200009F:rsa routines:RSA_padding_check_PKCS1_type_2:pkcs decoding error:crypto/rsa/rsa_pk1.c:269:
407D290301000000:error:02000072:rsa routines:rsa_ossl_private_decrypt:padding check failed:crypto/rsa/rsa_ossl.c:499:

问题原因

你的代码犯了核心错误:只将AES加密后的文件内容写入输出文件,但未写入EVP_SealInit生成的加密AES密钥(ek)和初始化向量(iv)。

EVP_Seal系列函数的工作逻辑是:

  1. 随机生成临时AES密钥和IV
  2. 用RSA公钥加密该AES密钥(得到ek)
  3. 用临时AES密钥+IV加密原始数据

解密必须遵循反向流程:

  1. 用RSA私钥解密ek得到原始AES密钥
  2. 用AES密钥+IV解密AES加密的数据

你直接用rsautl解密整个test.enc,相当于告诉OpenSSL“整个文件都是RSA公钥加密的单个数据块”,但实际文件是AES加密的数据流,完全不符合RSA解密的格式要求,因此触发填充校验错误。


修复方案

步骤1:修改加密代码,写入必要元数据

修改evp_aes_encrypt函数,在写入AES密文前,先写入加密密钥长度、加密密钥本身、初始化向量,同时释放ek的动态内存:

static int evp_aes_encrypt(char *in_path, char *out_path, EVP_PKEY *pkey)
{
    FILE *in_file = fopen(in_path, "rb");
    if (!in_file)
        return -1;

    FILE *out_file = fopen(out_path, "wb");
    if (!out_file)
    {
        fclose(in_file);
        return -1;
    }

    EVP_CIPHER_CTX* ctx = EVP_CIPHER_CTX_new();
    if (!ctx)
    {
        fclose(in_file);
        fclose(out_file);
        return -1;
    }

    int len;
    unsigned char iv[EVP_MAX_IV_LENGTH];
    unsigned char* ek = NULL;

    if (EVP_SealInit(ctx, EVP_aes_256_cbc(), &ek, &len, iv, &pkey, 1) != 1)
    {
        printf("EVP_SealInit failed\n");
        EVP_CIPHER_CTX_free(ctx);
        fclose(in_file);
        fclose(out_file);
        return -1;
    }

    // 写入加密密钥长度、密钥数据、IV
    fwrite(&len, sizeof(int), 1, out_file);
    fwrite(ek, 1, len, out_file);
    fwrite(iv, 1, EVP_CIPHER_iv_length(EVP_aes_256_cbc()), out_file);

    unsigned char in_buffer[BUFFER_SIZE];
    unsigned char out_buffer[BUFFER_SIZE + EVP_MAX_IV_LENGTH];

    int bytes_read, bytes_written;
    while ((bytes_read = fread(in_buffer, 1, BUFFER_SIZE, in_file)) > 0)
    {
        if (EVP_SealUpdate(ctx, out_buffer, &bytes_written, in_buffer, bytes_read) != 1) {
            printf("EVP_SealUpdate failed\n");
            OPENSSL_free(ek);
            EVP_CIPHER_CTX_free(ctx);
            fclose(in_file);
            fclose(out_file);
            return -1;
        }
        fwrite(out_buffer, 1, bytes_written, out_file);
    }

    if (EVP_SealFinal(ctx, out_buffer, &bytes_written) != 1) {
        printf("EVP_SealFinal failed\n");
        OPENSSL_free(ek);
        EVP_CIPHER_CTX_free(ctx);
        fclose(in_file);
        fclose(out_file);
        return -1;
    }

    fwrite(out_buffer, 1, bytes_written, out_file);

    // 释放动态分配的加密密钥内存
    OPENSSL_free(ek);
    EVP_CIPHER_CTX_free(ctx);
    fclose(in_file);
    fclose(out_file);

    return 0;
}

步骤2:编写对应解密代码

无法再用rsautl直接解密,需编写C代码读取元数据并完成解密:

static int evp_aes_decrypt(char *in_path, char *out_path, EVP_PKEY *pkey)
{
    FILE *in_file = fopen(in_path, "rb");
    if (!in_file)
        return -1;

    FILE *out_file = fopen(out_path, "wb");
    if (!out_file)
    {
        fclose(in_file);
        return -1;
    }

    EVP_CIPHER_CTX* ctx = EVP_CIPHER_CTX_new();
    if (!ctx)
    {
        fclose(in_file);
        fclose(out_file);
        return -1;
    }

    int len;
    // 读取加密密钥长度
    fread(&len, sizeof(int), 1, in_file);
    unsigned char ek[len];
    // 读取加密密钥
    fread(ek, 1, len, in_file);
    // 读取IV
    unsigned char iv[EVP_CIPHER_iv_length(EVP_aes_256_cbc())];
    fread(iv, 1, sizeof(iv), in_file);

    if (EVP_OpenInit(ctx, EVP_aes_256_cbc(), ek, len, iv, pkey) != 1)
    {
        printf("EVP_OpenInit failed\n");
        EVP_CIPHER_CTX_free(ctx);
        fclose(in_file);
        fclose(out_file);
        return -1;
    }

    unsigned char in_buffer[BUFFER_SIZE];
    unsigned char out_buffer[BUFFER_SIZE + EVP_MAX_IV_LENGTH];

    int bytes_read, bytes_written;
    while ((bytes_read = fread(in_buffer, 1, BUFFER_SIZE, in_file)) > 0)
    {
        if (EVP_OpenUpdate(ctx, out_buffer, &bytes_written, in_buffer, bytes_read) != 1) {
            printf("EVP_OpenUpdate failed\n");
            EVP_CIPHER_CTX_free(ctx);
            fclose(in_file);
            fclose(out_file);
            return -1;
        }
        fwrite(out_buffer, 1, bytes_written, out_file);
    }

    if (EVP_OpenFinal(ctx, out_buffer, &bytes_written) != 1) {
        printf("EVP_OpenFinal failed\n");
        EVP_CIPHER_CTX_free(ctx);
        fclose(in_file);
        fclose(out_file);
        return -1;
    }

    fwrite(out_buffer, 1, bytes_written, out_file);

    EVP_CIPHER_CTX_free(ctx);
    fclose(in_file);
    fclose(out_file);

    return 0;
}

// 解密调用示例(替换main函数)
// int main(void)
// {
//     FILE *pri = fopen("key.pem", "rb");
//     EVP_PKEY *pkey = PEM_read_PrivateKey(pri, NULL, NULL, NULL);
//     evp_aes_decrypt("test.enc", "test.dec", pkey);
//     return 0;
// }

编译与运行

编译时链接OpenSSL库:

gcc encrypt_decrypt.c -o encrypt_decrypt -lcrypto
  • 加密:运行编译后的程序(确保main函数调用evp_aes_encrypt)
  • 解密:修改main函数调用evp_aes_decrypt后重新编译,运行即可得到解密后的test.dec

内容的提问来源于stack exchange,提问作者Ivan Nikolsky

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.15 22:49:51