将普通公钥转换为PEM

时间:2019-05-20 10:23:28

标签: openssl type-conversion pem

我已经使用Prime-256v1从受信任的应用程序生成了一个EC密钥对,并将公钥导出到Normal OS。密钥大小为65个字节。公钥为纯格式(仅十六进制密钥)。

需要将导出的公共密钥提供给图书馆(第三方)。图书馆希望使用PEM格式的公钥。

经过一段时间的搜索,我的理解是首先将纯键转换为DER格式,然后将结果转换为PEM。但是我找不到用于将纯键转换为DER或PEM的任何API。

找到了该API,其中PEM_ASN1_write((i2d_of_void *)i2d_PUBKEY,PEM_STRING_PUBLIC,outfile,ctx-> cert-> key-> public_key,NULL,NULL,0,NULL,NULL);从文件指针转换。但是我无法执行文件操作,因为无法进行文件存储。我正在缓冲区中获取公钥。

如果有任何示例代码或API可以将纯十六进制键转换为PEM,我将在C程序中执行此操作。

预先感谢

2 个答案:

答案 0 :(得分:1)

使用openssl实用程序,您可以使用的命令是:

  

openssl ec -in。\ prime256pubkey.cer -pubin -inform der -pubout -outform pem -out。\ prime256pubkey.pem

要使用代码重现此代码,您需要使用以下主要的openssl api

一个openssl示例,围绕C openssl API变成了C ++代码,将是:

template<typename T, typename D>
std::unique_ptr<T, D> make_handle(T* handle, D deleter)
{
    return std::unique_ptr<T, D>{handle, deleter};
}

bool convert_der_ec_pubkey_to_pem()
{
    // read in DER ec public key
    auto infile = make_handle(BIO_new_file("prime256pubkey.cer", "rb"), BIO_free);
    if(!infile) return false;

    auto const eckey = make_handle(d2i_EC_PUBKEY_bio(infile.get(), nullptr), EC_KEY_free);
    if(!eckey) return false;

    infile.reset();

    // write out PEM ec public key
    auto outfile = make_handle(BIO_new_file("prime256pubkey.pem", "w"), BIO_free);
    if(!outfile) return false;

    return PEM_write_bio_EC_PUBKEY(outfile.get(), eckey.get()) != 0;
}

答案 1 :(得分:1)

您在注释4bb5f0c58cc71806ec4d228b730dd252947e679cce05f71d434787fe228f14c799cf8965780bb308aa722ac179bfa5fd57592a72cbdcfe89ab61ad5d77251186d中提供的值长度错误。它是129个十六进制数字(又名半字节),但是prime256v1(即secp256r1或P-256)的编码点必须是以04(未压缩)开头的65个八位位组或以02或03(压缩)开头的33个八位位组。当十六进制字符串(或十进制或八进制)表示整数时,您可以删除或添加前导零而不更改数字,但是EC点是 not 整数。

您说您的代码提供了65个字节,这对于未压缩是正确的。用那个

与大多数其他PK算法不同,OpenSSL没有针对ECC的算法特定的DER(因此是PEM)格式,因此任何被准确描述为需要PEM公钥(而不是)证书的文件,是一种传统的公共密钥传输方式),必须使用X.509 / PKIX SubjectPublicKeyInfo格式,OpenSSL将其命名为PUBKEY(如Shane的回答所示),并从或映射到{程序。要从原始公共点构造它,必须首先将其与曲线标识结合起来以形成实际的EC公钥,然后将其标识为EC以形成 generic 公钥,然后编写它。 OpenSSL可以使用“ mem”类型的BIO在内存中进行I / O(包括PEM)操作,而无需任何文件。 (并且将其牢牢地定为编程问题,而不是命令行问题,尽管这里仍然有很多问题,但这确实是题外话并属于另一个堆栈)。

EVP_PKEY

(ADDED)另外,由于X9.62点编码对于给定曲线是固定大小的,所以给定曲线的DER编码SPKI结构实际上由固定的标头和点值组成,因此您可以将其与固定标头并进行通用PEM转换:输出破折号-BEGIN行,输出带换行符的base64,输出破折号-END行。虽然如果知道ASN.1的工作原理就不难弄清楚报头,但是一种捷径是用例如ASCII码为虚拟密钥生成SPKI编码。 /* SO #56218946 */ #include <stdio.h> #include <stdlib.h> #include <openssl/ec.h> #include <openssl/err.h> #include <openssl/evp.h> #include <openssl/objects.h> #include <openssl/pem.h> #ifdef _WIN32 #include <openssl/applink.c> #endif void err (const char *label){ // for test; improve for real code fprintf (stderr, "Error in %s:\n", label); ERR_print_errors_fp (stderr); exit (1); } int main (void) //(int argc, char**argv) { ERR_load_crypto_strings(); /* or SSL_load_error_strings */ //OPENSSL_add_all_algorithms_noconf(); /* for PKCS#8 */ // test data -- replace for real use char hex [] = "04bb5f0c58cc71806ec4d228b730dd252947e679cce05f71d434787fe228f14c799cf8965780bb308aa722ac179bfa5fd57592a72cbdcfe89ab61ad5d77251186d"; unsigned char raw [65]; for( int i = 0; i < 65; i++ ){ sscanf(hex+2*i, "%2hhx", raw+i); } EC_KEY *eck = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1); /* or OBJ_txt2nid("prime256v1") */ if( !eck ) err("ECCnewbyname"); EC_KEY_set_asn1_flag(eck, OPENSSL_EC_NAMED_CURVE); /* needed below 1.1.0 */ const unsigned char *ptr = raw; if( !o2i_ECPublicKey (&eck, &ptr, sizeof(raw)) ) err("o2iECPublic=point"); EVP_PKEY * pkey = EVP_PKEY_new(); if( !EVP_PKEY_assign_EC_KEY(pkey, eck) ) err("PKEYassign"); BIO *bio = BIO_new(BIO_s_mem()); if( !PEM_write_bio_PUBKEY (bio, pkey) ) err("PEMwrite"); char *pem = NULL; long len = BIO_get_mem_data (bio, &pem); fwrite (pem, 1, len, stdout); // for test; for real use as needed return 0; } 并删除最后65个字节(如果使用openssl ecparam -genkey -name prime256v1 -outform der压缩则为33)。与Loading raw 64-byte long ECDSA public key in Java中的Java变体进行比较。