【问题标题】:how to import external rsa public key in openssl如何在openssl中导入外部rsa公钥
【发布时间】:2018-06-08 06:59:55
【问题描述】:

我想通过从外部服务器接收到的 RSA 公钥文件执行 RSA 签名验证等操作。 键值以der格式或pem格式给出,如下所示。

der:"30820122300d06092a864886f70d01010105000382010f003082010a02820101008ab8c549138cc59f605b50265f69ec3b8ce3b3e9af5e174d26cfe242650104bea05101189676c7a292cc6b5ae719e119e3ac29e9d3ad9dadcda496f2f7185f9c0c4872a2db124f01992b238e83fd582d8a290f2973f9cf744f1e53f8aa53a225c12299b1cc3658fb607cb5aba579832d6c2687f71300a4df3df1c1407e17d22a5c19c830ed0c5824072309a612bb3e4fb339f25bbbcfe2999f4342110649abac5f4bfea2a59cd38c173979a679afdc8baacb4e87eb50acf806cfb7407504bdac110cfbcb99c04227031e146b9f3b8377c87035690309fae9872e2c7b93e8375fccdebc4f98be1d574269c513a43594f6b8861f7464832ae99ebaceae0fcc3ac50203010001"

pem_base64:"MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAirjFSROMxZ9gW1AmX2nsO4zjs+mvXhdNJs/iQmUBBL6gUQEYlnbHopLMa1rnGeEZ46wp6dOtna3NpJby9xhfnAxIcqLbEk8BmSsjjoP9WC2KKQ8pc/nPdE8eU/iqU6IlwSKZscw2WPtgfLWrpXmDLWwmh/cTAKTfPfHBQH4X0ipcGcgw7QxYJAcjCaYSuz5PsznyW7vP4pmfQ0IRBkmrrF9L/qKlnNOMFzl5pnmv3Iuqy06H61Cs+AbPt0B1BL2sEQz7y5nAQicDHhRrnzuDd8hwNWkDCfrphy4se5PoN1/M3rxPmL4dV0JpxROkNZT2uIYfdGSDKumeus6uD8w6xQIDAQAB"

我想把上面的数据导入到"struct rsa_st * rsa",但是不行。

例如,要导入 der 格式的 rsa 公钥,我做了以下操作:

==========================================================
char data[] = "30820122300d06092a864886f70d01010105000382010f003082010a02820101008ab8c549138cc59f605b50265f69ec3b8ce3b3e9af5e174d26cfe242650104bea05101189676c7a292cc6b5ae719e119e3ac29e9d3ad9dadcda496f2f7185f9c0c4872a2db124f01992b238e83fd582d8a290f2973f9cf744f1e53f8aa53a225c12299b1cc3658fb607cb5aba579832d6c2687f71300a4df3df1c1407e17d22a5c19c830ed0c5824072309a612bb3e4fb339f25bbbcfe2999f4342110649abac5f4bfea2a59cd38c173979a679afdc8baacb4e87eb50acf806cfb7407504bdac110cfbcb99c04227031e146b9f3b8377c87035690309fae9872e2c7b93e8375fccdebc4f98be1d574269c513a43594f6b8861f7464832ae99ebaceae0fcc3ac50203010001";

unsigned char * pArr = (unsigned char *)malloc(buf_len);
RSA *pub_rsa = NULL;

fnStr2Hex(pArr, data); // Converts a data array composed of strings to a hex array (pArr).
pub_rsa=d2i_RSAPublicKey(NULL,&pArr,(long)buf_len);
==========================================================

在这种情况下,d2i_RSAPublicKey 函数返回 NULL 指针。 我不知道出了什么问题。

而且我也不知道怎么把pem_base64收到的字符串数据改成"struct rsa_st * rsa"。 示例代码使用了一个名为 "ReadPublicKey" 的函数,它似乎加载了一个 X.509 证书文件。

我不读取文件,但我需要像上面一样从服务器获取数据。

请知道的人回答一下。

【问题讨论】:

  • 你确定你有一个公钥而不是 x509 公钥证书

标签: openssl rsa


【解决方案1】:

您的数据不是(只是)由 PKCS1 定义的重新发布为 RFC8017 的 RSA 公钥,而是由 X.509 定义的 X.509 SubjectPublicKeyInfo 重新发布为RFC5280 包含 RSA 公钥由RFC3279 定义。 OpenSSL 将此 X.509 结构称为 PUBKEY。 使用d2i_RSA_PUBKEY

还有:

  • 您发布的代码未显示buf_len 的来源。确保它是正确的。

  • 您发布的代码将pArr 设置为指向malloc 的空间,然后将&pArr 作为ppin 传递给d2i 例程。 d2i 例程更改 ppin 指向的指针,因此它不再指向 malloc 的空间。如果您现在尝试free(pArr),根据 C 标准,这是未定义的行为,实际上可能会崩溃或损坏您的堆,并导致其他看似无关的事情出错或失败;如果你不free,那就是内存泄漏。

  • PEM 格式不仅仅是 DER 的 base64。它是 DER 的 base64,分成由行终止符终止的行,并添加了标题和尾行(破折号、BEGIN/END、类型、破折号);这些是格式的一部分,不是可选的。可以选择使用 822 样式的标头块,但这不适用于此处。 DER 的 Base64 可能很有用,但它不是 PEM。


元:请在您的问题中添加有关您的问题的其他信息。 Stackexchange 旨在将问题包含在问题中,将答案包含在答案中,以及其他原因,因为答案没有按时间顺序保存。我以为有一个关于此的帮助中心页面,但如果是这样,我现在找不到它。

无论如何,你说你的“当前”代码是:

void fnStr2Hex(char* out, char* in) { 
        int data_len = strlen(in); 
        char * pStr = in; 
        int i; 

        for(i=0; i<data_len/2; i++) { 
                char buf[3] = {0,}; 
                memcpy(buf, pStr, sizeof(buf)); 

                out[i] = (unsigned char)strtol(buf, NULL, 16); 

                // need to check strol 2nd arguments... for error checking.. 
                printf("i:%d, pArr[i]:%02X \n", i, out[i]); 

                pStr+=2; 
        } 
} 

int main() { 
        char raw_data[] = 
"30819F300D06092A864886F70D010101050003818D0030818902818100AA18ABA43B50DEEF38598FAF87D2AB634E4571C130A9BCA7B878267414FAAB8B471BD8965F5C9FC3818485EAF529C26246F3055064A8DE19C8C338BE5496CBAEB059DC0B358143B44A35449EB264113121A455BD7FDE3FAC919E94B56FB9BB4F651CDB23EAD439D6CD523EB08191E75B35FD13A7419B3090F24787BD4F4E19670203010001"; 

        int data_len = strlen(raw_data);  
        unsigned char * pArr = (unsigned char *)malloc(data_len); 
        memset(pArr, 0x00, data_len); 

// raw_data is a string. Not in hex state. So I changed the contents of 
raw_data [] to hex in pArr. 
// The implementation of this function is above main function. 
        fnStr2Hex(pArr, raw_data); 

        STDout=BIO_new_fp(stdout,BIO_NOCLOSE); 

        pub_rsa=d2i_RSAPublicKey(NULL,&pArr,(long)data_len); 

        if(pub_rsa == NULL) { 
                printf(&quot;error : failed d2i_RSAPublicKey \n&quot;); 
                return -1; 
        } 

        BN_print(STDout,pub_rsa->n);   // print modulus bignum 
        BN_print(STDout,pub_rsa->e);  //  print exponent bignum 

        return 0; 
} 

这是不可编译的;它有一个损坏的评论,没有声明STDoutpub_rsa,也没有需要的#include。解决这些问题会导致以下问题:

  1. (big)你的十六进制转换错误。它一次从十六进制字符串复制 3 个字符,因此它虽然转换后的字节应该是十六进制 30 81 9F 等,但它实际上在我的系统(可能是大多数)08 19 F3 等上计算,这是完全错误的。由于它访问任何有效对象之外的至少一个字节,理论上它可能做得更糟。如果您查看,您的“检查”printf 会向您显示。

  2. (小)你的分配是错误的。首先,在 C 中永远不需要强制转换 malloc(或 realloccalloc)的返回值,有时(但不是这里)是有害的; see the comp.lang.c FAQ。其次,虽然 hex 字符串是 324 个字符(加上终止的 null),但您只需要 162 个字节即可包含解码结果(也由您的“检查”printf 显示)。然而,由于d2i 忽略了多余的尾随数据,这并没有什么坏处。另外,正如我之前描述的那样,您仍然有释放它的问题。

  3. (big) 正如我之前所说,d2i_RSAPublicKey 是错误的例程;它需要您没有的特定数据格式(PKCS1)。 此数据的正确例程是d2i_RSA_PUBKEY

  4. (小)你的错误处理很差。当 OpenSSL 例程返回失败指示时,几乎在所有情况下,它还会在“错误队列”中存储详细的错误信息。显示此信息的最简单方法是使用ERR_print_errors_fp,但如果您愿意,还有更复杂的方法。为获得最佳结果,您通常需要预先加载错误字符串,通常由 ERR_load_crypto_strings 或 1.1.0+ OPENSSL_init_crypto 或其中之一的变体也涵盖 SSL(您当前未使用且不需要)。尽管在这种情况下,在修复 d2i_RSAPublicKey 之前,错误队列会为您提供 ASN.1 解码例程的内部描述,您可能无论如何都不会理解 - 尽管它可能至少为您指明了方向查看 ASN.1 格式。

带有这些修复的代码确实正确地将十六进制的模数和指数打印为:

AA18ABA43B50DEEF38598FAF87D2AB634E4571C130A9BCA7B878267414FAAB8B471BD8965F5C9FC3818485EAF529C26246F3055064A8DE19C8C338BE5496CBAEB059DC0B358143B44A35449EB264113121A455BD7FDE3FAC919E94B56FB9BB4F651CDB23EAD439D6CD523EB08191E75B35FD13A7419B3090F24787BD4F4E196710001

由于您没有放置任何分隔符,因此您必须知道每个部分的长度才能正确阅读。

但是,这在 OpenSSL 版本 1.1.0 或更高版本中不起作用。 API 已更改,因此您可以 no longer directly access fields like n and e in RSA(这是一个 typedef ror struct rsa_st)。

然后您为 PEM 格式提出以下建议:

raw_data[] = { 
"-----BEGIN PUBLIC KEY-----"\ 
"MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAirjFSROMxZ9gW1AmX2ns"\ 
"O4zjs+mvXhdNJs/iQmUBBL6gUQEYlnbHopLMa1rnGeEZ46wp6dOtna3NpJby9xhf"\ 
"nAxIcqLbEk8BmSsjjoP9WC2KKQ8pc/nPdE8eU/iqU6IlwSKZscw2WPtgfLWrpXmD"\ 
"LWwmh/cTAKTfPfHBQH4X0ipcGcgw7QxYJAcjCaYSuz5PsznyW7vP4pmfQ0IRBkmr"\ 
"rF9L/qKlnNOMFzl5pnmv3Iuqy06H61Cs+AbPt0B1BL2sEQz7y5nAQicDHhRrnzuD"\ 
"d8hwNWkDCfrphy4se5PoN1/M3rxPmL4dV0JpxROkNZT2uIYfdGSDKumeus6uD8w6"\ 
"xQIDAQAB"\ 
"-----END PUBLIC KEY----- " };

这也是错误的。除了缺少数据类型(正式声明说明符)char,预处理器换行语法

"ABC"\
"DEF"\
"GHI"

完全等价于

"ABC" "DEF" "GHI" 

因为相邻的字符串字面量是完全等价的

"ABCDEFGHI"

并且不包含任何需要的行终止符。您需要将至少 LF 字符,理想情况下是 CR 和 LF 字符,in 放入字符串值,在 C 中只能通过使用转义序列 \n\r 以便携方式完成. (虽然 PEM 规范要求 CR LF,但 OpenSSL 仅适用于 LF。)因此您需要:

char raw_data[] = { "-----BEGIN PUBLIC KEY-----\n"
"MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAirjFSROMxZ9gW1AmX2ns\n"
"O4zjs+mvXhdNJs/iQmUBBL6gUQEYlnbHopLMa1rnGeEZ46wp6dOtna3NpJby9xhf\n"
"nAxIcqLbEk8BmSsjjoP9WC2KKQ8pc/nPdE8eU/iqU6IlwSKZscw2WPtgfLWrpXmD\n"
"LWwmh/cTAKTfPfHBQH4X0ipcGcgw7QxYJAcjCaYSuz5PsznyW7vP4pmfQ0IRBkmr\n"
"rF9L/qKlnNOMFzl5pnmv3Iuqy06H61Cs+AbPt0B1BL2sEQz7y5nAQicDHhRrnzuD\n"
"d8hwNWkDCfrphy4se5PoN1/M3rxPmL4dV0JpxROkNZT2uIYfdGSDKumeus6uD8w6\n"
"xQIDAQAB\n"
"-----END PUBLIC KEY-----\n" };

(或者没有{ } 也一样,因为用单个字符串字面量初始化一个字符数组是由C作为一种特殊情况处理的)。

接下来,您的代码尝试将此数据从十六进制转换,与前一种情况相同——但 PEM 是 not 十六进制,因此即使在如上所述修复了十六进制例程之后,这也是完全错误的。此数据已采用 PEM 格式,因此您只需使用 BIO_new_mem_buf(raw_data, strlen(raw_data)) - 或作为优化 BIO_new_mem_buf(raw_data,-1) 为您执行 strlen

最后,这个问题和上面一样:你调用PEM_read_bio_RSAPublicKey,但是你得到的数据不是PKCS1格式的;而是打电话给PEM_read_bio_RSA_PUBKEY

您的代码通过这些更改再次起作用。

既然如此,我不会问你为什么不想为此使用命令行功能——即使它只需要一两秒钟,而不是几天。

【讨论】:

  • 谢谢 dave_thompson,我有更多问题,所以我发布如下。
【解决方案2】:

我这样做的目的是在 RSA 中找到模数和指数 公钥。 我的示例代码和当前结果如下所示。

================================================ ==========

void fnStr2Hex(char* out, char* in) { 
        int data_len = strlen(in); 
        char * pStr = in; 
        int i; 

        for(i=0; i<data_len/2; i++) { 
                char buf[3] = {0,}; 
                memcpy(buf, pStr, sizeof(buf)); 

                out[i] = (unsigned char)strtol(buf, NULL, 16); 

                // need to check strol 2nd arguments... for error checking.. 
                printf("i:%d, pArr[i]:%02X \n", i, out[i]); 

                pStr+=2; 
        } 
} 

int main() { 
        char raw_data[] = 
"30819F300D06092A864886F70D010101050003818D0030818902818100AA18ABA43B50DEEF38598FAF87D2AB634E4571C130A9BCA7B878267414FAAB8B471BD8965F5C9FC3818485EAF529C26246F3055064A8DE19C8C338BE5496CBAEB059DC0B358143B44A35449EB264113121A455BD7FDE3FAC919E94B56FB9BB4F651CDB23EAD439D6CD523EB08191E75B35FD13A7419B3090F24787BD4F4E19670203010001"; 

        int data_len = strlen(raw_data);  
        unsigned char * pArr = (unsigned char *)malloc(data_len); 
        memset(pArr, 0x00, data_len); 

// raw_data is a string. Not in hex state. So I changed the contents of 
raw_data [] to hex in pArr. 
// The implementation of this function is above main function. 
        fnStr2Hex(pArr, raw_data); 

        STDout=BIO_new_fp(stdout,BIO_NOCLOSE); 

        pub_rsa=d2i_RSAPublicKey(NULL,&pArr,(long)data_len); 

        if(pub_rsa == NULL) { 
                printf(&quot;error : failed d2i_RSAPublicKey \n&quot;); 
                return -1; 
        } 

        BN_print(STDout,pub_rsa->n);   // print modulus bignum 
        BN_print(STDout,pub_rsa->e);  //  print exponent bignum 

        return 0; 
} 

结果:错误:d2i_RSAPublicKey 失败 我不知道为什么它失败了。

你说你需要前缀和后缀来做 PEM 格式。 如下图配置对吗?

raw_data[] = { 
"-----BEGIN PUBLIC KEY-----"\ 
"MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAirjFSROMxZ9gW1AmX2ns"\ 
"O4zjs+mvXhdNJs/iQmUBBL6gUQEYlnbHopLMa1rnGeEZ46wp6dOtna3NpJby9xhf"\ 
"nAxIcqLbEk8BmSsjjoP9WC2KKQ8pc/nPdE8eU/iqU6IlwSKZscw2WPtgfLWrpXmD"\ 
"LWwmh/cTAKTfPfHBQH4X0ipcGcgw7QxYJAcjCaYSuz5PsznyW7vP4pmfQ0IRBkmr"\ 
"rF9L/qKlnNOMFzl5pnmv3Iuqy06H61Cs+AbPt0B1BL2sEQz7y5nAQicDHhRrnzuD"\ 
"d8hwNWkDCfrphy4se5PoN1/M3rxPmL4dV0JpxROkNZT2uIYfdGSDKumeus6uD8w6"\ 
"xQIDAQAB"\ 
"-----END PUBLIC KEY----- " };

之后,我编码如下。

    int data_len = strlen(raw_data); 
        BIO *bufio = NULL; 
        RSA *pub_rsa = NULL; 

        unsigned char * pArr = (unsigned char *)malloc(data_len); 
        memset(pArr, 0x00, data_len); 

        fnStr2Hex(pArr, raw_data);  // for converting hex 

        bufio = BIO_new_mem_buf((void*)pArr, data_len); 

        if(bufio == NULL) { 
                printf("Error (1) \n"); 
                return -1; 
        } 

        PEM_read_bio_RSAPublicKey(bufio, &pub_rsa, 0, NULL); 

        if(pub_rsa == NULL) { 
                printf("Error (2) \n"); 
                return -1; 
        } 
} // end of main 

当我执行上面的代码时,错误(2)被输出。 我想在上述两种(DER、PEM)情况下得到帮助。 同样,我想在 RSA 公钥中找到模数和公共指数。 (我想在 C 代码中实现它,而不是在 shell 命令中。)

BR,

【讨论】:

  • 这属于问题而不是答案。请参阅我的答案的更新。
猜你喜欢
  • 2010-10-04
  • 1970-01-01
  • 2021-10-08
  • 1970-01-01
  • 2018-10-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多