这是对文件test.txttest.enc进行加密的代码. 密钥对key.pemkey.pub是使用openssl:

openssl genrsa -out key.pem
openssl rsa -in key.pem -out key.pub -pubout
#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:

填充物似乎有问题,但我不知道如何修复它. 先谢谢你.

推荐答案

"看起来填充物有问题,"--实际上问题是你只做了一半.

EVP_Seal/Open执行hybrid encryption,也称为信封(或信封)加密.这是described almost correctly in wikipedia,但我将重新表述,以匹配OpenSSL实现和我的首选项.

加密包括三个步骤和two different次加密:

  1. 为对称算法生成随机数密钥(称为DEK)
  2. 使用RSA和收件人的公钥(您必须已经拥有公钥)对DEK进行加密(因此这可能被认为是第一步,但不需要为每条消息重复)
  3. 使用使用DEK的对称密码(在您的示例中为AES-256-CBC)加密数据,这取决于模式,有时是随机IV/随机数(CBC使用IV);也取决于模式,这可能会额外产生身份验证标签(CBC不会)

加密步骤1和2可以按任一顺序完成,但我认为这种顺序更常见,而且肯定是OpenSSL中使用的顺序(SealInit表示0,1表示1,SealUpdateSealFinal加在一起表示2).

To decrypt you need all 2-4 data items由步骤1和2产生:RSA加密的DEK、IV(如果适用)、加密的数据和标签(如果适用).在真实的系统中,有数据 struct 来打包这些数据项,外加附加元数据:Wikipedia提到了PKCS7(现在被CMS及其变体SMIME取代)和PGP;还有Jose/JWE、XMLenc等等.解密同样需要two steps:

  1. 使用RSA和本地已知私钥对传输的加密DEK进行解密
  2. 使用相同的对称算法、解密的DEK、传输IV/随机数(如果适用)和传输标签(如果适用)来解密传输的加密数据

(这一次你必须按这个顺序做).

然而,要解密with OpenSSL commandline,您需要将这些项放在单独的文件中(身份验证模式的标记除外,这是命令行enc根本无法处理的).因此,我修改了您的程序,将加密的dek、IV和密文写入3个文件,并略微重构以减少混乱,更改文件名以避免冲突,并添加最少的错误处理:

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

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

#define BUFFER_SIZE 1024

static int evp_aes_encrypt(FILE *fin, FILE *fek, FILE *fiv, FILE *fenc, EVP_PKEY *pkey)
{
    EVP_CIPHER_CTX* ctx = EVP_CIPHER_CTX_new();
    if (!ctx) return -1;

    unsigned char iv[EVP_MAX_IV_LENGTH];
    unsigned char* ek = malloc(EVP_PKEY_get_size(pkey));
    int ekl, ivl;

    if (EVP_SealInit(ctx, EVP_aes_256_cbc(), &ek, &ekl, iv, &pkey, 1) != 1)
    { EVP_CIPHER_CTX_free(ctx); free(ek); return -2; }
    ivl = 16; // or EVP_CIPHER_CTX_get_iv_length(EVP_aes_256_cbc());
    fwrite (ek,ekl,1,fek); fwrite (iv,ivl,1,fiv);

    unsigned char in_buffer[BUFFER_SIZE];
    unsigned char out_buffer[BUFFER_SIZE + EVP_MAX_IV_LENGTH];
    // actually this only needs + cipher_blocksize, but bytes are cheap now

    int bytes_read, bytes_written;
    while ((bytes_read = fread(in_buffer, 1, BUFFER_SIZE, fin)) > 0)
    {
        if (EVP_SealUpdate(ctx, out_buffer, &bytes_written, in_buffer, bytes_read) != 1) {
            EVP_CIPHER_CTX_free(ctx); free(ek); return -3; }
        fwrite(out_buffer, 1, bytes_written, fenc);
    }

    if (EVP_SealFinal(ctx, out_buffer, &bytes_written) != 1) {
        EVP_CIPHER_CTX_free(ctx); free(ek); return -4; }

    fwrite(out_buffer, 1, bytes_written, fenc);
    EVP_CIPHER_CTX_free(ctx); free(ek); 
    return 0;
}

int main(void)
{
    FILE *pub = fopen("76696165.pub", "rb");
    EVP_PKEY *pkey = PEM_read_PUBKEY(pub, NULL, NULL, NULL);
    FILE *fin = fopen("76696165.in", "rb");
    FILE *fek = fopen("76696165.ek", "wb");
    FILE *fiv = fopen("76696165.iv", "wb");
    FILE *fenc = fopen("76696165.enc", "wb");
    int err = evp_aes_encrypt(fin, fek, fiv, fenc, pkey);
    if( err ){ printf ("error in step %d\n", err);
        ERR_print_errors_fp(stdout); return 1; }
    // fclose not needed when exiting normally
    return 0;
}

运行此程序后,我得到了以下信息:

$ ls -rt1 76696165*
76696165.pub
76696165.prv
76696165.c
76696165.exe
76696165.in
76696165.iv
76696165.enc
76696165.ek
$ # STEP 1
$ openssl rsautl -decrypt -inkey 76696165.prv <76696165.ek >76696165.dek
$ xxd 76696165.dek; xxd 76696165.iv
0000000: a46e 5084 e28b 8f9c 7fe3 a465 fbeb 4ed7  .nP........e..N.
0000010: 7c4b 38f5 3174 f905 3034 52df d992 da3d  |K8.1t..04R....=
0000000: 959b a611 3fbb 5486 b6c6 2056 f9ee 6f03  ....?.T... V..o.
$ # STEP 2
$ keyhex=$(xxd -p -c32 <76696165.dek) ivhex=$(xxd -p <76696165.iv) 
$ openssl enc -aes-256-cbc -d -K $keyhex -iv $ivhex <76696165.enc
Once upon a midnight dreary, while I pondered, weak and weary,
Over many a quaint and curious volume of forgotten lore—
While I nodded, nearly napping, suddenly there came a tapping,
As of some one gently rapping, rapping at my chamber door.
"'Tis some visiter," I muttered, "tapping at my chamber door—
            Only this and nothing more."
-- Edgar Allan Poe

C++相关问答推荐

位屏蔽对于无符号转换是强制的吗?

C语言中字符数组声明中的标准

数据包未从DPDK端口传输到内核端口

为什么在C中进行大量的位移位?

当execvp在C函数中失败时杀死子进程

C++中矢量类型定义和数据保护的高效解决方案

`#if`条件中是否允许`sizeof`?

如何在C中打印包含扫描字符和整数的语句?

X64:并发写入布尔数组

SSH会话出现意外状态

S将C语言宏定义为自身的目的是什么?(在glibc标题中看到)

是否需要包括<;errno.h>;才能使用perror?

==284==错误:AddressSaniizer:堆栈缓冲区下溢

如何在VSCode中创建和使用我自己的C库?

通过k&;r语法的c声明无效

C程序向服务器发送TCPRST

向左移位3如何得到以字节为单位的位数?

问题:C#Define上的初始值设定项元素不是常量

变量值不正确的问题

使用 SDL2 的 C 程序中的内存泄漏