C: OpenSSL RSA_private_decrypt() fails with "error:0407A079:rsa routines:RSA_padding_check_PKCS1_OAEP:oaep decoding error”

C: OpenSSL RSA_private_decrypt() fails with "error:0407A079:rsa routines:RSA_padding_check_PKCS1_OAEP:oaep decoding error”

我是密码学的新手,所以我决定创建一个简单的程序来打开一个文件加密数据,将其放入 etest.txt,然后打开这个文件解密并放入 etest.txt.我知道这听起来很奇怪,但它是出于教育目的。所以这是我的代码。我已经阅读了很多关于此问题的主题,但其中 none 对我有用。

#include <openssl/rsa.h>
#include <openssl/pem.h>
#include <openssl/err.h>
#include <stdio.h>
#include <string.h>

int main(void) {
    size_t pri_len;            // Length of private key
    size_t pub_len;            // Length of public key
    char   *pri_key;           // Private key
    char   *pub_key;           // Public key
    char   *msg = malloc(256);  // Message to encrypt
    char   *encrypt = NULL;    // Encrypted message
    char   *decrypt = NULL;    // Decrypted message
    char   *err;               // Buffer for any error messages
    size_t red;

    RSA *keypair = RSA_generate_key(2048, 3, NULL, NULL);
     FILE *in   = fopen("test.txt", "r");
     FILE *out  = fopen("etest.txt", "w");

     if(in == NULL)
     {  
        printf("in Error is %d (%s).\n", errno, strerror(errno));
     }
     if(out == NULL)
     {  
        printf("out Error is %d (%s).\n", errno, strerror(errno));
     }

    encrypt = malloc(RSA_size(keypair));

    for(;;)
    {
        red = fread(msg, 1, RSA_size(keypair)-42, in);
        if((RSA_public_encrypt(RSA_size(keypair)-42, (unsigned char*)msg, (unsigned char*)encrypt,
                                             keypair, RSA_PKCS1_OAEP_PADDING)) == -1) {
            ERR_load_crypto_strings();
            ERR_error_string(ERR_get_error(), err);
            fprintf(stderr, "Error encrypting message: %s\n", err);
        }

        if(fwrite(encrypt, 1, strlen(encrypt), out) == 1)
        {
           printf("fwrite Error is %d (%s).\n", errno, strerror(errno));
        }

        if(feof(in))
        {
            break;
        }       
    }

    fclose(in);
    fclose(out);

    in   = fopen("etest.txt", "r");
    out  = fopen("dtest.txt", "w");

     if(in == NULL)
     {        
        printf("in Error is %d (%s).\n", errno, strerror(errno));
     }

     if(out == NULL)
     {        
        printf("out Error is %d (%s).\n", errno, strerror(errno));
     }

    decrypt = malloc(RSA_size(keypair));

    for(;;)
    {
         red = fread(msg, 1, 256, in);
         if(RSA_private_decrypt(red, (unsigned char*)msg, (unsigned char*)decrypt,
                               keypair, RSA_PKCS1_OAEP_PADDING) == -1) {

            ERR_load_crypto_strings();
            ERR_error_string(ERR_get_error(), err);
            fprintf(stderr, "Error decrypting message: %s\n", err);
        }        

        fwrite(decrypt, 1, strlen(decrypt), out);

        if(feof(in))
        {
          break;
        }
    }
    fclose(in);
    fclose(out);     
    RSA_free(keypair);
    return 0;
}

当我 运行 编码时,它返回错误提示:Error decrypting message: error:0407A079:rsa routines:RSA_padding_check_PKCS1_OAEP:oaep decoding error 对不起,如果我的问题听起来很愚蠢。希望你能帮忙。谢谢。

这里有一些错误。首先当你阅读和加密时:

red = fread(msg, 1, RSA_size(keypair)-42, in);
if((RSA_public_encrypt(RSA_size(keypair)-42, (unsigned char*)msg, (unsigned char*)encrypt,
                                     keypair, RSA_PKCS1_OAEP_PADDING)) == -1) {

调用 fread 不一定会读取请求的字节数,可能会 return 0。所以当你到达文件末尾时,你会加密更多字节比你需要的。因此,传入 red 作为要加密的字节数。此外,首先检查 red 是否为 0,如果是,则 break 退出循环:

red = fread(msg, 1, RSA_size(keypair)-42, in);
if (red == 0) break;
if(((red=RSA_public_encrypt(RSA_size(keypair)-42, (unsigned char*)msg, (unsigned char*)encrypt,
                                     keypair, RSA_PKCS1_OAEP_PADDING))) == -1) {

请注意,我们正在保存 RSA_public_encrypt 的 return 值。这在您将加密数据写入磁盘的地方发挥作用:

if(fwrite(encrypt, 1, strlen(encrypt), out) == 1)

encrypt 是字符数组,不是字符串。这意味着它不是 NULL 终止的,并且它可能包含 NULL 字节。所以你不能使用strlen。相反,捕获 RSA_public_encrypt 的 return 值并将其作为要写入的大小传递:

if(fwrite(encrypt, 1, red, out) == 1)

因为我们正在检查 fread 的 return 值以跳出循环,所以不需要这样做:

if(feof(in))
{
    break;
}

有关使用 feof 的危险,请参阅 this post

然后当你读回加密数据时是这样的:

 red = fread(msg, 1, 256, in);
 if(RSA_private_decrypt(red, (unsigned char*)msg, (unsigned char*)decrypt,
                       keypair, RSA_PKCS1_OAEP_PADDING) == -1) {

RSA_private_decrypt 需要一个长度为 RSA_size(keypair) 的加密块。所以从磁盘中读取那么多字节并将那么多字节传递给函数。另外,如果没有得到预期的数量,请检查 freadbreak 的 return 值,并捕获 RSA_private_decrypt 的 return 值:

 red = fread(msg, 1, RSA_size(keypair), in);
 if (red < RSA_size(keypair)) break;
 if((red=RSA_private_decrypt(red, (unsigned char*)msg, (unsigned char*)decrypt,
                       keypair, RSA_PKCS1_OAEP_PADDING)) == -1) {

稍后将解密数据写入磁盘时:

fwrite(decrypt, 1, strlen(decrypt), out);

虽然被解密的可能是一个字符串(如果您的输入文件是纯文本),returned 数据不是 NULL 终止的,所以显式地写很多字节而不是使用 strlen :

fwrite(decrypt, 1, red, out);

最后,与加密循环一样,解密循环中不需要:

if(feof(in))
{
    break;
}

应用这些修复后,您应该会得到预期的结果。