Openssl 将普通公钥转换为PEM

Openssl 将普通公钥转换为PEM,openssl,type-conversion,pem,Openssl,Type Conversion,Pem,我使用Prime-256v1从受信任的应用程序生成了一个EC密钥对,并将公钥导出到普通操作系统。密钥大小为65字节。公钥采用普通格式(仅十六进制) 导出的公钥需要提供给库(第三方)。库需要PEM格式的公钥 经过一段时间的搜索,我的理解是首先将普通密钥转换为DER格式,然后将结果转换为PEM。但是我还没有找到任何从普通密钥转换到DER或PEM的API 找到PEM_ASN1_编写的此API((i2d_of_void*)i2d_PUBKEY,PEM_STRING_PUBLIC,outfile,ctx-

我使用Prime-256v1从受信任的应用程序生成了一个EC密钥对,并将公钥导出到普通操作系统。密钥大小为65字节。公钥采用普通格式(仅十六进制)

导出的公钥需要提供给库(第三方)。库需要PEM格式的公钥

经过一段时间的搜索,我的理解是首先将普通密钥转换为DER格式,然后将结果转换为PEM。但是我还没有找到任何从普通密钥转换到DER或PEM的API

找到PEM_ASN1_编写的此API((i2d_of_void*)i2d_PUBKEY,PEM_STRING_PUBLIC,outfile,ctx->cert->key->PUBLIC_key,NULL,NULL,0,NULL,NULL);它从文件指针转换而来。但我无法进行文件操作,因为无法进行文件存储。我正在缓冲区中获取公钥

我在C程序中这样做,如果有任何示例代码或API将普通十六进制键转换为PEM


提前感谢使用openssl实用程序,您可以使用的命令是:

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

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

  • -读入DER格式的EC公钥
  • -以PEM格式写出EC公钥
一个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;
}
模板
std::唯一的ptr生成句柄(T*handle,D deleter)
{
返回std::unique_ptr{handle,deleter};
}
bool convert_der_ecu pubkey_to_pem()
{
//读入公钥
自动填充=生成句柄(BIO_新文件(“prime256pubkey.cer”、“rb”),无BIO_文件);
如果(!infle)返回false;
auto const eckey=make_handle(d2i_EC_PUBKEY_bio(infle.get(),nullptr),EC_KEY_free);
如果(!eckey)返回false;
infle.reset();
//写出PEM ec公钥
自动输出文件=make_handle(BIO_new_文件(“prime256pubkey.pem”,“w”),BIO_free);
如果(!outfile)返回false;
返回PEM_write_bio_EC_PUBKEY(outfile.get(),eckey.get())!=0;
}

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

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

与大多数其他PK算法不同,OpenSSL没有用于ECC的特定于算法的DER(因此也没有PEM)格式,因此准确描述为需要PEM公钥(而不是证书,这是传输公钥的传统方式)的任何东西都必须使用X.509/PKIX SubjectPublicKeyInfo格式,哪个OpenSSL命名为
PUBKEY
(如Shane的回答所示),并映射到程序中的
EVP_PKEY
结构。要从原始公共点构造该公共点,必须首先将其与曲线标识组合以形成实际的EC公钥,然后将其标识为EC以形成通用公钥,并将其写入。OpenSSL可以使用“mem”类型的BIO进行内存的I/O(包括PEM),而无需任何文件。(这是一个明确的编程问题,而不是命令行问题,这实际上是一个离题的问题,属于另一个堆栈,尽管这里还是有很多问题。)

/*SO#56218946*/
#包括
#包括
#包括
#包括
#包括
#包括
#包括
#ifdef_WIN32
#包括
#恩迪夫
void err(const char*label){//用于测试;改进用于实际代码
fprintf(标准,“%s:\n中的错误”,标签);
错误打印错误fp(stderr);
出口(1);
}
int main(void)/(int argc,char**argv)
{
ERR_load_crypto_strings();/*或SSL_load_error_strings*/
//用于PKCS的OPENSSL_add_all_算法_noconf();/*8*/
//测试数据——替换以供实际使用
字符十六进制[]=“04BB5F0C58CC71806EC4D228B730DD252947E679CCE05F7AD434787FE228F14C799CF8965780BB308AA722AC179BFA5FD57592A72CBDE89AB61AD5D77251186D”;
无符号字符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);/*或OBJ_txt2nid(“prime256v1”)*/
如果(!eck)错误(“eccnewyname”);
EC_KEY_set_asn1_flag(eck,OPENSSL_EC_NAMED_CURVE);/*需要在1.1.0以下*/
常量无符号字符*ptr=raw;
如果(!o2i_ECPublicKey(&eck,&ptr,sizeof(raw)))错误(“o2iECPublic=point”);
EVP_PKEY*PKEY=EVP_PKEY_new();
如果(!EVP_PKEY_assign_EC_KEY(PKEY,eck))错误(“PKEYassign”);
BIO*BIO=BIO_new(BIO_s_mem());
如果(!PEM_write_bio_PUBKEY(bio,pkey))出错(“pemswrite”);
char*pem=NULL;long len=BIO_get_mem_data(BIO,&pem);
fwrite(pem,1,len,stdout);//用于测试;根据需要用于实际使用
返回0;
}

(新增)或者,由于X9.62点编码对于给定曲线的大小是固定的,因此给定曲线的DER编码SPKI结构实际上包含一个固定标题,后跟点值,因此您可以改为与该固定标题连接并进行通用PEM转换:输出虚线开始行,输出base64带换行符,输出破折号结束行。虽然如果知道ASN.1是如何工作的,就不难计算出报头,但一个快捷方式是使用例如
openssl ecparam-genkey-name prime256v1-outform der
为伪密钥生成SPKI编码,并删除最后65个字节(如果使用
-conv_form
进行压缩,则删除33个字节)。与上的Java变体进行比较

什么是使你处理?它是在哪里定义的?它是一个围绕std:unique\u ptr的简单包装器,用于将OpenSSL原始指针类型转换为raii类。它只是使
/* 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;
}