如何在OpenSSL中导入外部RSA公钥



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

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

我想将上述数据导入"结构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);
==========================================================

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

而且我不知道如何将pem_base64收到的字符串数据更改为"结构rsa_st * rsa"。示例代码使用一个名为"ReadPublicKey">的函数,该函数似乎加载了一个 X.509 证书文件。

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

请回答知道此事的人。

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

也:

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

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

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


Meta:请在您的问题中添加有关您的问题的其他信息。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. (大)您的十六进制转换是错误的。它一次从十六进制字符串复制 3 个字符,因此虽然转换后的字节应该是十六进制 30 81 9F 等,但它实际上是在我的系统(可能大多数)08 19 F3 等上计算的,这是完全错误的。由于它访问任何有效对象之外的至少一个字节,因此理论上它可以做得更糟。如果你看一下,你的"检查"printf会告诉你这一点。

  2. (小)你的分配是错误的。首先,在 C 中永远不需要强制转换malloc(或realloccalloc)的返回值,有时(但不是在这里)有害的;请参阅 comp.lang.c 常见问题解答。其次,尽管十六进制字符串是 324 个字符(加上终止 null),但您只需要 162 个字节即可包含解码结果(也由您的"检查"printf显示)。但是,由于d2i忽略了多余的尾随数据,因此不会造成任何伤害。另外,您仍然有我之前描述的释放它的问题。

  3. (大)正如我之前所说,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 已更改,因此您无法再直接访问RSA中的ne等字段(这是一个 typedef rorstruct 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 字符,这在 C 中只能通过使用转义序列nr来移植完成。(尽管PEM规范要求CR LF,但OpenSSL仅适用于LF。因此,您需要:

char raw_data[] = { "-----BEGIN PUBLIC KEY-----n"
"MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAirjFSROMxZ9gW1AmX2nsn"
"O4zjs+mvXhdNJs/iQmUBBL6gUQEYlnbHopLMa1rnGeEZ46wp6dOtna3NpJby9xhfn"
"nAxIcqLbEk8BmSsjjoP9WC2KKQ8pc/nPdE8eU/iqU6IlwSKZscw2WPtgfLWrpXmDn"
"LWwmh/cTAKTfPfHBQH4X0ipcGcgw7QxYJAcjCaYSuz5PsznyW7vP4pmfQ0IRBkmrn"
"rF9L/qKlnNOMFzl5pnmv3Iuqy06H61Cs+AbPt0B1BL2sEQz7y5nAQicDHhRrnzuDn"
"d8hwNWkDCfrphy4se5PoN1/M3rxPmL4dV0JpxROkNZT2uIYfdGSDKumeus6uD8w6n"
"xQIDAQABn"
"-----END PUBLIC KEY-----n" };

(或者没有{ }相同,因为使用单个字符串文本初始化字符数组由 C 作为特殊情况处理)。

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

最后,这具有与上述相同的问题:您调用PEM_read_bio_RSAPublicKey但您拥有的数据不是 PKCS1 格式;而是调用PEM_read_bio_RSA_PUBKEY

同样,具有这些更改的代码可以正常工作。

由于这是SO,我不问你为什么不想为此使用命令行功能 - 即使它只需要一两秒钟,而不是几天。

我这样做的目的是在 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 命令中实现它。

溴,

最新更新