将普通公钥转换为 PEM

问题描述 投票:0回答:3

我已经使用 Prime-256v1 从受信任的应用程序生成了 EC 密钥对,并将公钥导出到普通操作系统。密钥大小为 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);从文件指针转换而来。但我无法进行文件操作,因为无法进行文件存储。我正在缓冲区中获取公钥。

我正在 C 程序中执行此操作,如果有任何示例代码或 API 将普通十六进制密钥转换为 PEM。

提前致谢

openssl type-conversion pem
3个回答
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
投票

您在评论

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

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

与大多数其他 PK 算法不同,OpenSSL 没有用于 ECC 的特定于算法的 DER(以及 PEM)格式,因此任何准确描述为需要 PEM 公钥(而不是证书,这是传输密钥的传统方式)的任何内容。 publickey)必须使用 X.509/PKIX subjectPublicKeyInfo 格式,OpenSSL 将其命名为 PUBKEY(如 Shane 的答案所示)并映射到程序中的

EVP_PKEY
结构。要从原始公共点构建此密钥,您必须首先将其与曲线标识相结合以形成实际的 EC 公钥,然后将其标识为 EC 以形成
generic
公钥,并写入它。 OpenSSL 可以使用“mem”类型的 BIO 与内存进行 I/O(包括 PEM),而无需任何文件。 (并让这成为一个编程问题,而不是命令行问题,这确实是题外话,属于另一个堆栈,尽管无论如何,这里都会问很多问题。) /* 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; }

(添加)或者,由于 X9.62 点编码对于给定曲线来说是固定大小的,因此给定曲线的 DER 编码 SPKI 结构实际上由固定标头后跟点值组成,因此您可以与该固定标头连接header 并进行通用 PEM 转换:输出破折号 - BEGIN 行,输出带有换行符的 base64,输出破折号 - END 行。虽然如果知道 ASN.1 的工作原理,那么计算出标头并不困难,但一种捷径是使用例如以下命令为虚拟密钥生成 SPKI 编码: 
openssl ecparam -genkey -name prime256v1 -outform der

并删除最后 65 个字节(如果使用

-conv_form
压缩则为 33 个字节)。与
在 Java 中加载原始 64 字节长 ECDSA 公钥
中的 Java 变体进行比较。


0
投票

X

i.ytimg.com

36%

开始公钥

MIIBIDANBgkqhkLCSMOBAQEFAAOCAQRAMIIBCAKCAQEA7gSCIRYNTHRwgFoySFBV 7Bq9CUzHqNF7ttcXYINtfg5e131SRfce@kbnuliaH3htY22BAot VukB9Ek+r2nR

结束公钥

公钥:(2048 位)模数:

08:ce:86:10:21:16:00:4c:c4:70:00:52:32:48:5 15:ec:la:bd:09:4cc7al:d1:7b:b6d7:17: 60:83: 6d:7e:84:9:07:76:69:45:17:10:06:46:46:bb:在:87:06:18:6:63:6:54:02: 88:be:36:09:01:14:49:3e:af:69:d1:01:bc:62:Bc:23:72:cd:fc:f2:9b:a2:13:851dd:64:编辑:b2:56:ccif70dtcd:61:74:80 / c:4c15b12d:@a:c1:17:17:eb:09:30:51:99:ef:c0:20:18:72:69:72 :00:45:6ftel:a3:55:33:71:19:41:bf:

© www.soinside.com 2019 - 2024. All rights reserved.