首页 > 解决方案 > d2i_RSA_PUBKEY 总是返回 NULL

问题描述

我必须处理一些与 AIX 相关的 Openssl 错误并尝试编写一些测试代码。简而言之,我想在缓冲区中读取 RSA 密钥文件,然后从缓冲区中获取数据以进行一些检查。当我运行测试程序时,我总是得到“错误:RSA 为空”,这意味着缓冲区是空的。

我用这个命令行创建了 Testkeys:

openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048 -outform DER -out testprivate_key.pem
openssl rsa -in testprivate_key.pem -inform DER -outform DER -pubout -out testpublic_key.pem

我必须承认我对 Openssl 编程一无所知,因此非常欢迎任何提示。

#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>

#include <openssl/pem.h>
#include <openssl/err.h>                                        /* ERR_* */
#include <openssl/rand.h>                                       /* RAND_* */
#include <openssl/bn.h>
#include <openssl/bio.h>
#include <openssl/rsa.h>
#include <openssl/evp.h>


unsigned char buf[1024*10];

/* const char keyfiles[][32] = { "testpublic_key32.pem", "testpublic_key64.pem" }; */

int get_buffer () {
   RSA *pub_key = NULL;
   /* FILE *key_file; */
   /* key_file = fopen( keyfiles[0],"rt"); */
   FILE *key_file = fopen("testpublic_key.pem","r"); // read in key file
   if (!key_file)
   {
     printf("Failed to open key file\n");
    exit(1);
   }
   pub_key = PEM_read_RSA_PUBKEY(key_file,&pub_key,NULL,NULL); // fill buffer
   BIO *mem = BIO_new(BIO_s_mem());
   RSA_print(mem,pub_key,0);
   BIO_read(mem,buf,1024*10);

   BUF_MEM *bio_buf=NULL;
   BIO_get_mem_ptr(mem,&bio_buf);
   RSA_free(pub_key);
   BIO_free(mem);


}


int get_key(const unsigned char *buf, int len) {

    RSA *rsa = d2i_RSA_PUBKEY(NULL, &buf, len); // get data from buffer and do some checks
    if (rsa != NULL) {
        if (rsa->e != NULL) {
            printf("BN : <%s> (hex) -- <%s> (dec)\n", BN_bn2hex(rsa->e), BN_bn2dec(rsa->e));
            if (BN_is_odd(rsa->e) == 0) {
                printf("Error : RSA public exponent is even\n");
            } else {
                printf("RSA public exponent is OK.\n");
                return 0;
            }
        }
        RSA_free(rsa);
    }
       else {
        printf("Error : RSA is NULL\n");
    }
    return 1;
}

int main() {
    get_buffer();
    return get_key(buf, sizeof buf);
}

编译字符串:

gcc -lcrypto -o openssl_odd_even openssl_odd_even.c
/home/packagebuilder/test/openssl_bignum_issue # ./openssl_odd_even
Error : RSA is NULL

标签: copensslaix

解决方案


当您可以直接在 RSA 结构中轻松引用已加载的密钥时,我不确定为什么要将密钥扔到一些基本缓冲区中。但是假设您有一些理由,让我们从基础开始并解释沿途发生的事情。

通过 CLI 生成密钥

您可以以任何您想要的形式(PEM 或 DER)生成密钥对。对我们来说,我们想要 PEM,所以我们必须使用 genpkey 接口。如果我们想要 DER,我们可以只使用 genrsa 接口,这要容易得多。无论如何,语法看起来像这样。请注意以 PEM 形式生成输出的特定请求(从而使我们诚实地命名 .pem 文件):

openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048 -outform PEM -out testprivate_key.pem

这应该会生成 testprivate_key.pem,它看起来像这样:

-----BEGIN PRIVATE KEY-----
... base64 encoding of the key pair here ...
-----END PRIVATE KEY-----

接下来,我们需要这个密钥对中的公钥。我们也可以使用 openssl rsa CLI 来获得它。

openssl rsa -in testprivate_key.pem -inform PEM -pubout -out testpublic_key.pem -outform PEM

这应该会创建 testpublic_key.pem,它应该如下所示:

-----BEGIN PUBLIC KEY-----
... bas64 encoding of public key here ...
-----END PUBLIC KEY-----

这会处理关键文件的内容。上代码。


从 PEM 编码文件中读取 RSA 公钥

一旦我们有了这些文件,我们就可以使用 openssl libcrypto(对于 Windows,它的 libeay32)将其中任何一个读入程序。这个简单的示例应用程序从当前工作目录读取 PEM 编码的公钥,然后将其以 DER 形式存储在内存缓冲区中,然后使用BIO_dump(来自 bio api 的一个非常方便的实用程序函数)将该内存缓冲区转储到标准输出:

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

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

int main()
{
    OpenSSL_add_all_algorithms();
    OpenSSL_add_all_ciphers();

    // read the PEM from disk (assumes current working directory)
    FILE *fp = fopen("testpublic_key.pem", "r");
    if (fp == NULL)
    {
        perror("testpublic_key.pem");
        return EXIT_FAILURE;
    }

    // load from disk
    RSA *pub_key = PEM_read_RSA_PUBKEY(fp, NULL, NULL, NULL);
    if (pub_key == NULL)
    {
        perror("Failed to load RSA public key from PEM file");
        return EXIT_FAILURE;
    }

    // no longer need this
    fclose(fp);

    // from here we can use pub_key however we want. in this case we're
    //  goin ot store it in a memory buffer in DER form.
    unsigned char buff[4*1024], *p = buff;
    int len = i2d_RSA_PUBKEY(pub_key, &p);
    if (len > 0)
    {
        // show the content of the buffer in the console using BIO-dump
        BIO* bio = BIO_new_fp(stdout, BIO_NOCLOSE);
        BIO_dump(bio, (const char*)buff, len);
        BIO_flush(bio);
        BIO_free(bio);
    }

    RSA_free(pub_key);

    return EXIT_SUCCESS;
}

编译它并从我们之前创建的密钥文件位于 locatd 的工作目录运行,将为我们提供类似于以下内容的内容:

样本输出(显然不同)

0000 - 30 82 01 22 30 0d 06 09-2a 86 48 86 f7 0d 01 01   0.."0...*.H.....
0010 - 01 05 00 03 82 01 0f 00-30 82 01 0a 02 82 01 01   ........0.......
0020 - 00 9c b5 e2 ff c0 1b e8-c1 4d cc bb 76 c1 8b d6   .........M..v...
0030 - eb b6 ec 92 a3 e3 38 82-50 16 13 3d 2c bc ef 49   ......8.P..=,..I
0040 - 21 3c d6 83 ae 4d be b7-d5 7c 67 11 84 a4 ed 4e   !<...M...|g....N
0050 - 86 b4 c8 41 3e c4 70 e5-a1 cf 9d 13 26 6c bf f2   ...A>.p.....&l..
0060 - 5c 7e 4f 04 a6 0e e0 9d-90 55 87 67 e7 f8 58 62   \~O......U.g..Xb
0070 - a6 ff 85 a0 99 68 62 58-0b 02 66 74 3d f6 19 05   .....hbX..ft=...
0080 - 7d e3 51 fa b1 c2 db e2-f3 e8 fa f5 5f 36 95 67   }.Q........._6.g
0090 - cf 8f eb 32 7e 39 5d e1-37 30 57 5a 1d 25 9d fa   ...2~9].70WZ.%..
00a0 - ad 50 63 f5 23 14 b2 2a-de 10 7d b8 7e 83 2b b9   .Pc.#..*..}.~.+.
00b0 - 8c 8b aa 73 7b 4a 91 be-68 5d d8 ad d0 76 e0 de   ...s{J..h]...v..
00c0 - 15 bc c6 9a 77 f2 31 a9-11 e3 b7 83 ce ae e2 96   ....w.1.........
00d0 - 6b 9c 2b 20 b9 e5 d7 22-27 46 10 2b 91 5c a3 67   k.+ ..."'F.+.\.g
00e0 - 7e ea 8c d0 69 e1 06 0e-eb 1a a4 dd 22 b9 5e f8   ~...i.......".^.
00f0 - f1 8a db 73 86 57 f2 d5-d1 70 10 24 f7 08 1d 76   ...s.W...p.$...v
0100 - 14 2e de d0 47 95 ce ac-52 fc 4a 16 c5 19 29 cd   ....G...R.J...).
0110 - 94 40 f2 23 4d 63 03 5f-10 8a 21 6b 5d 5a 3c 30   .@.#Mc._..!k]Z<0
0120 - 1d 02 03 01 00 01                                 ......

现在,您可以将此缓冲区与您的get_key函数一起使用来检查密钥本身的部分(尽管您为什么想要我不知道,因为您已经拥有RSA*从 PEM 加载的较早版本)。修改源代码以添加您的get_key函数及其对公钥的检查为我们提供了以下信息:

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

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

int get_key(const unsigned char *buf, int len)
{
    int result = 1;

    RSA *rsa = d2i_RSA_PUBKEY(NULL, &buf, len);
    if (rsa != NULL)
    {
        if (rsa->e != NULL)
        {
            printf("BN : <%s> (hex) -- <%s> (dec)\n", BN_bn2hex(rsa->e), BN_bn2dec(rsa->e));
            if (BN_is_odd(rsa->e) == 0)
            {
                printf("Error : RSA public exponent is even\n");
            }
            else
            {
                printf("RSA public exponent is OK.\n");
                result = 0;
            }
        }
        RSA_free(rsa);
    }
    else
    {
        printf("Error : RSA is NULL\n");
    }
    return result;
}

int main()
{
    OpenSSL_add_all_algorithms();
    OpenSSL_add_all_ciphers();

    // read the PEM from disk (assumes current working directory)
    FILE *fp = fopen("testpublic_key.pem", "r");
    if (fp == NULL)
    {
        perror("testpublic_key.pem");
        return EXIT_FAILURE;
    }

    // load from disk
    RSA *pub_key = PEM_read_RSA_PUBKEY(fp, NULL, NULL, NULL);
    if (pub_key == NULL)
    {
        perror("Failed to load RSA public key from PEM file");
        return EXIT_FAILURE;
    }

    // no longer need this
    fclose(fp);

    // from here we can use pub_key however we want. in this case we're
    //  goin ot store it in a memory buffer in DER form.
    unsigned char buff[4*1024], *p = buff;
    int len = i2d_RSA_PUBKEY(pub_key, &p);
    if (len > 0)
    {
        // show the content of the buffer in the console using BIO-dump
        BIO* bio = BIO_new_fp(stdout, BIO_NOCLOSE);
        BIO_dump(bio, (const char*)buff, len);
        BIO_flush(bio);
        BIO_free(bio);
    }

    RSA_free(pub_key);

    // run get_key
    printf("get_key returned %d\n", get_key(buff, len));

    return EXIT_SUCCESS;
}

输出

0000 - 30 82 01 22 30 0d 06 09-2a 86 48 86 f7 0d 01 01   0.."0...*.H.....
0010 - 01 05 00 03 82 01 0f 00-30 82 01 0a 02 82 01 01   ........0.......
0020 - 00 9c b5 e2 ff c0 1b e8-c1 4d cc bb 76 c1 8b d6   .........M..v...
0030 - eb b6 ec 92 a3 e3 38 82-50 16 13 3d 2c bc ef 49   ......8.P..=,..I
0040 - 21 3c d6 83 ae 4d be b7-d5 7c 67 11 84 a4 ed 4e   !<...M...|g....N
0050 - 86 b4 c8 41 3e c4 70 e5-a1 cf 9d 13 26 6c bf f2   ...A>.p.....&l..
0060 - 5c 7e 4f 04 a6 0e e0 9d-90 55 87 67 e7 f8 58 62   \~O......U.g..Xb
0070 - a6 ff 85 a0 99 68 62 58-0b 02 66 74 3d f6 19 05   .....hbX..ft=...
0080 - 7d e3 51 fa b1 c2 db e2-f3 e8 fa f5 5f 36 95 67   }.Q........._6.g
0090 - cf 8f eb 32 7e 39 5d e1-37 30 57 5a 1d 25 9d fa   ...2~9].70WZ.%..
00a0 - ad 50 63 f5 23 14 b2 2a-de 10 7d b8 7e 83 2b b9   .Pc.#..*..}.~.+.
00b0 - 8c 8b aa 73 7b 4a 91 be-68 5d d8 ad d0 76 e0 de   ...s{J..h]...v..
00c0 - 15 bc c6 9a 77 f2 31 a9-11 e3 b7 83 ce ae e2 96   ....w.1.........
00d0 - 6b 9c 2b 20 b9 e5 d7 22-27 46 10 2b 91 5c a3 67   k.+ ..."'F.+.\.g
00e0 - 7e ea 8c d0 69 e1 06 0e-eb 1a a4 dd 22 b9 5e f8   ~...i.......".^.
00f0 - f1 8a db 73 86 57 f2 d5-d1 70 10 24 f7 08 1d 76   ...s.W...p.$...v
0100 - 14 2e de d0 47 95 ce ac-52 fc 4a 16 c5 19 29 cd   ....G...R.J...).
0110 - 94 40 f2 23 4d 63 03 5f-10 8a 21 6b 5d 5a 3c 30   .@.#Mc._..!k]Z<0
0120 - 1d 02 03 01 00 01                                 ......
BN : <010001> (hex) -- <65537> (dec)
RSA public exponent is OK.
get_key returned 0

删除不需要的代码

现实情况是,您首先不需要缓冲中间体,我希望这是显而易见的。只需从磁盘加载正确制作的 PEM,并使用由此产生的结果RSA*来检查公共指数。结果很简单:

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

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

int check_key_exponent(RSA *rsa)
{
    int result = 1;

    if (rsa && rsa->e)
    {
        printf("BN : <%s> (hex) -- <%s> (dec)\n", BN_bn2hex(rsa->e), BN_bn2dec(rsa->e));
        if (BN_is_odd(rsa->e) == 0)
        {
            printf("Error : RSA public exponent is even\n");
        }
        else
        {
            printf("RSA public exponent is OK.\n");
            result = 0;
        }
    }
    else
    {
        printf("Error : RSA is NULL\n");
    }
    return result;
}

int main()
{
    OpenSSL_add_all_algorithms();
    OpenSSL_add_all_ciphers();

    // read the PEM from disk (assumes current working directory)
    FILE *fp = fopen("testpublic_key.pem", "r");
    if (fp == NULL)
    {
        perror("testpublic_key.pem");
        return EXIT_FAILURE;
    }

    // load from disk
    RSA *pub_key = PEM_read_RSA_PUBKEY(fp, NULL, NULL, NULL);
    if (pub_key == NULL)
    {
        perror("Failed to load RSA public key from PEM file");
        return EXIT_FAILURE;
    }

    // no longer need this
    fclose(fp);

    // run get_key
    printf("check_key_exponent returned %d\n", check_key_exponent(pub_key));

    RSA_free(pub_key);

    return EXIT_SUCCESS;
}

输出

BN : <010001> (hex) -- <65537> (dec)
RSA public exponent is OK.
check_key_exponent returned 0

推荐阅读