您的数据不是(只是)由 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("error : failed d2i_RSAPublicKey \n");
return -1;
}
BN_print(STDout,pub_rsa->n); // print modulus bignum
BN_print(STDout,pub_rsa->e); // print exponent bignum
return 0;
}
这是不可编译的;它有一个损坏的评论,没有声明STDout 和pub_rsa,也没有需要的#include。解决这些问题会导致以下问题:
-
(big)你的十六进制转换错误。它一次从十六进制字符串复制 3 个字符,因此它虽然转换后的字节应该是十六进制 30 81 9F 等,但它实际上在我的系统(可能是大多数)08 19 F3 等上计算,这是完全错误的。由于它访问任何有效对象之外的至少一个字节,理论上它可能做得更糟。如果您查看,您的“检查”printf 会向您显示。
-
(小)你的分配是错误的。首先,在 C 中永远不需要强制转换 malloc(或 realloc 或 calloc)的返回值,有时(但不是这里)是有害的; see the comp.lang.c FAQ。其次,虽然 hex 字符串是 324 个字符(加上终止的 null),但您只需要 162 个字节即可包含解码结果(也由您的“检查”printf 显示)。然而,由于d2i 忽略了多余的尾随数据,这并没有什么坏处。另外,正如我之前描述的那样,您仍然有释放它的问题。
-
(big) 正如我之前所说,d2i_RSAPublicKey 是错误的例程;它需要您没有的特定数据格式(PKCS1)。 此数据的正确例程是d2i_RSA_PUBKEY。
-
(小)你的错误处理很差。当 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。
您的代码通过这些更改再次起作用。
既然如此,我不会问你为什么不想为此使用命令行功能——即使它只需要一两秒钟,而不是几天。