使用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系列函数的工作逻辑是:
- 随机生成临时AES密钥和IV
- 用RSA公钥加密该AES密钥(得到
ek) - 用临时AES密钥+IV加密原始数据
解密必须遵循反向流程:
- 用RSA私钥解密
ek得到原始AES密钥 - 用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
相关产品推荐
相关产品推荐

