如何在openssl

时间:2018-06-08 06:59:55

标签: openssl rsa

我想通过从外部服务器收到的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证书文件。

我没有阅读该文件,但我需要从上面的服务器获取数据。

请回答了解此事的人。

2 个答案:

答案 0 :(得分:1)

您的数据不是(仅)由PKCS1重新发布为RFC8017定义的RSA PublicKey,而是由X.509定义的X.509 SubjectPublicKeyInfo重新发布为RFC5280 包含 RFC3279定义的RSA公钥。 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。

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等,这是完全错误的。由于它访问任何有效对象之外的至少一个字节,理论上它可能会更糟。您的检查&#39;如果您查看它,printf会向您显示此信息。

  2. (小)您的分配错误。首先,在C中永远不需要转换malloc(或realloccalloc)的返回值,有时(但不是在这里)有害; see the comp.lang.c FAQ。第二,虽然 hex 字符串是324个字符(加上终止空值),但只需要162个字节来包含解码结果(也可以通过&#39;检查&#39; 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的变体(之前您不会使用和删除)来加载错误字符串。 #39; t need)。虽然在这种情况下,在修复d2i_RSAPublicKey之前,错误队列会为您提供ASN.1解码例程的内部描述,但您可能无法理解 - 尽管它可能至少指向了您在查看ASN.1格式的方向。

  5. 使用这些修补程序的代码可以正确地将十六进制模数和指数打印为:

    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和{ {1}}。 (尽管PEM规范要求使用CR LF,但OpenSSL只能使用LF。)因此,您需要:

    \r

    (或者在没有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 十六进制,所以即使修复了上面的十六进制例程,这也完全错误。此数据已采用PEM格式,因此您只需使用{ } - 或作为优化BIO_new_mem_buf(raw_data, strlen(raw_data))为您执行BIO_new_mem_buf(raw_data,-1)

    最后,这与上面的问题相同:你调用strlen,但你拥有的数据不是PKCS1格式;而是致电PEM_read_bio_RSAPublicKey

    再次使用这些更改的代码可以正常工作。

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

答案 1 :(得分:0)

我这样做的目的是找到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 

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

BR,