简体   繁体   English

d2i_RSA_PUBKEY始终返回NULL

[英]d2i_RSA_PUBKEY always returns NULL

I have to Work on some AIX related Openssl Bugs and tried to write some Testcode. 我必须处理一些与AIX相关的Openssl错误,并尝试编写一些Testcode。 In Short, i want to read in RSA Keyfiles in a Buffer and than get the Data out of the Buffer to do some checking. 简而言之,我想在缓冲区中读取RSA密钥文件,而不是从缓冲区中取出数据来进行一些检查。 When i run the Testprogram i always get "Error : RSA is NULL", which means the Buffer is empty. 当我运行Testprogram时,我总是收到“错误:RSA为NULL”,这意味着缓冲区为空。

I created the Testkeys with this commandline: 我使用以下命令行创建了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

I must admit i have no real clue about Openssl Programing so any Hints are highly welcome. 我必须承认我对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);
}

compile string: 编译字符串:

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

I'm not sure why you would want to throw the key around in some basic buffer when you can just as easily refer to the loaded key in the RSA structure directly. 我不确定为什么您可以直接在RSA结构中直接引用已加载的密钥时,为什么要在某个基本缓冲区中扔掉密钥。 But assuming you have some reason, lets start with the basics and explain what's going on along the way. 但是,假设您有某些原因,让我们从基础知识入手,并说明整个过程。

Key Generation via CLI 通过CLI生成密钥

You can generate the keypair in whatever form you want (PEM or DER). 您可以以任意形式(PEM或DER)生成密钥对。 For us, we want PEM, so we have to use the genpkey interface. 对于我们来说,我们需要PEM,因此我们必须使用genpkey接口。 If we wanted DER we could just use the genrsa interface, which is much easier. 如果我们想要DER,我们可以使用genrsa界面,这很容易。 Regardless, the syntax looks like this. 无论如何,语法看起来像这样。 Note the specific request to generate output in PEM form (thereby making us honest in our naming of a .pem file): 请注意以PEM格式生成输出的特定请求(从而使我们诚实地命名.pem文件):

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

That should generate testprivate_key.pem, which will look like this: 这将生成testprivate_key.pem,如下所示:

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

Next, we want the public key from this key pair. 接下来,我们需要该密钥对中的公共密钥。 We can use the openssl rsa CLI to get that as well. 我们也可以使用openssl rsa CLI来实现。

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

That should create testpublic_key.pem, and it should look like this: 那应该创建testpublic_key.pem,看起来应该像这样:

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

That takes care of the key file stuff. 这样就可以处理密钥文件。 On to the code. 转到代码。


Reading RSA public key from PEM encoded file 从PEM编码文件中读取RSA公钥

Once we have the files, we can read either of them into a program using openssl libcrypto (for Windows its libeay32). 有了文件后,我们可以使用openssl libcrypto(对于Windows为libeay32)将它们中的任何一个读入程序。 This simple sample application reads the PEM-encoded public key from the current working directory, then stores it in a memory buffer in DER form, then dumps that memory buffer to stdout using BIO_dump (a very handy utility function from the bio api): 这个简单的示例应用程序从当前工作目录中读取PEM编码的公钥,然后将其以DER形式存储在内存缓冲区中,然后使用BIO_dump (bio api中非常方便的实用函数)将该内存缓冲区转储到stdout:

#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;
}

Compiling that and running from the working directory where the key files we created earlier are locatd, will give us something similar to this: 编译该文件并从我们先前创建的密钥文件所在的工作目录中运行,将为我们提供类似于以下内容:

Sample Output (varies, obviously) 样本输出 (显然会有所不同)

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                                 ......

Now you can use this buffer with your with your get_key function to examine the parts of the key itself (though why you would want to I have no clue, since you already had the RSA* earlier when it was loaded from the PEM). 现在,您可以将这个缓冲区与get_key函数一起使用,以检查密钥本身的各个部分(尽管您不希望有任何线索,因为从PEM加载RSA*之前就已经有了它)。 Modifying the source to add your get_key function and its examination of the public key gives us this: 修改源代码以添加您的get_key函数及其对公钥的检查可以使我们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;
}

Output 产量

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

Removing Unneeded Code 删除不需要的代码

The reality is, you don't need that buffer intermediate in the first place, and I hope that's obvious. 现实情况是,您一开始不需要中间缓冲区,我希望这很明显。 Just load the properly crafted PEM from disk, and use the resulting RSA* from that to check the public exponent. 只需从磁盘加载适当制作的PEM,然后使用由此产生的RSA*来检查公共指数。 The result is simply this: 结果就是这样的:

#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;
}

Output 产量

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM