代码之家  ›  专栏  ›  技术社区  ›  Ivan Nikolsky

无法使用openssl解密C代码加密的文件

  •  0
  • Ivan Nikolsky  · 技术社区  · 2 年前

    这是对文件进行加密的代码 test.txt test.enc 。 密钥对 key.pem key.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:
    

    看起来填充有问题,但我不知道如何解决。 提前谢谢。

    0 回复  |  直到 2 年前
        1
  •  4
  •   dave_thompson_085    2 年前

    “看起来填充有问题,”——实际上问题是你只做了一半的工作。

    EVP_Seal/Open 混合加密 ,也称为信封(或信封)加密。这是 described almost correctly in wikipedia 但我会重新措辞,以匹配OpenSSL的实现和我的偏好。

    加密包括三个步骤和 两种不同的 加密:

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

    加密步骤1和2可以按任意顺序执行,但我相信这种顺序更常见,而且它肯定是OpenSSL中使用的顺序( SealInit 做0和1, SealUpdate SealFinal 一起做2)。

    要解密,您需要所有2-4个数据项 由步骤1和2生成:RSA加密的DEK、IV(如果适用)、加密的数据和标签(如果适用的话)。在实际系统中,有一些数据结构将这些数据项打包,再加上额外的元数据:维基百科提到PKCS7(现在被CMS及其变体SMIME取代)和PGP;还有JOSE/JWE、XMLenc等等。解密同样需要 两个步骤 :

    1. 使用RSA和本地已知私钥解密传输的加密DEK
    2. 使用相同的对称算法解密传输的加密数据,解密的DEK,如果适用,传输的IV/随机数,以及如果适用,发送的标签

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

    但是,要解密 使用OpenSSL命令行 您需要将这些项目放在单独的文件中(除了用于身份验证模式的标记,该命令行 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