【问题标题】:C: OpenSSL RSA_private_decrypt() fails with "error:0407A079:rsa routines:RSA_padding_check_PKCS1_OAEP:oaep decoding error”C:OpenSSL RSA_private_decrypt() 失败并出现“错误:0407A079:rsaroutines:RSA_padding_check_PKCS1_OAEP:oaep 解码错误”
【发布时间】:2016-03-06 16:53:13
【问题描述】:

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

#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 抱歉,如果我的问题听起来很愚蠢。希望你能帮忙。谢谢。

【问题讨论】:

  • 你有什么问题?
  • 它给了我错误。为什么会这样?

标签: c linux encryption openssl public-key-encryption


【解决方案1】:

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

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 的调用不一定会读取请求的字节数,并且可能返回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 的返回值。这在这里发挥作用,您将加密数据写入磁盘:

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

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

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

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

if(feof(in))
{
    break;
}

请参阅this post,了解使用feof 的风险。

那么当你读回加密数据的时候就是这样的:

 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的返回值,如果你没有得到预期的金额,并捕获RSA_private_decrypt的返回值:

 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);

虽然解密的内容可能是字符串(如果您的输入文件是纯文本),但返回的数据不是以 NULL 结尾的,因此请明确写入多字节而不是使用 strlen

fwrite(decrypt, 1, red, out);

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

if(feof(in))
{
    break;
}

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

【讨论】:

  • 非常感谢。如果您能回答,我还有其他问题。我可以用这个加密任何类型的文件吗?我的意思是如果我使用 example.jpg 而不是 test.txt 会起作用吗?
  • @Ojs 是的,这适用于任何文件,无论内容如何。这就是为什么不将数据视为字符串很重要的原因。
  • 请再来一个 :) 这个算法有多安全?
  • RSA 在与适当的填充(您正在使用)一起使用时通常是一种安全算法。请务必以安全的方式处理密钥。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-11-27
  • 1970-01-01
  • 2012-09-24
  • 1970-01-01
  • 2018-02-24
相关资源
最近更新 更多