首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >将普通公钥转换为PEM

将普通公钥转换为PEM
EN

Stack Overflow用户
提问于 2019-05-20 18:23:28
回答 1查看 2.6K关注 0票数 2

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

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

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

找到这个接口,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。

提前感谢

EN

回答 1

Stack Overflow用户

发布于 2019-05-28 15:03:26

您在注释4bb5f0c58cc71806ec4d228b730dd252947e679cce05f71d434787fe228f14c799cf8965780bb308aa722ac179bfa5fd57592a72cbdcfe89ab61ad5d77251186d中提供的值的长度错误。它是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),而不需要任何文件。(让这个问题坚定地成为编程问题,而不是命令行问题,这实际上是离题的,属于另一个堆栈,尽管这里提出了许多问题。)

代码语言:javascript
复制
/* 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结构实际上由一个固定的头部和后跟的点值组成,所以您可以改为连接该固定的头部,并执行通用的base64转换:输出短划线-开始行,输出带换行符的base64,输出短划线-结束线。虽然如果知道ASN.1是如何工作的,就不难算出报头,但一个捷径是使用例如openssl ecparam -genkey -name prime256v1 -outform der为虚拟密钥生成SPKI编码,并删除最后65个字节(或33个字节,如果使用-conv_form压缩)。与Loading raw 64-byte long ECDSA public key in Java上的Java变体进行比较。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/56218946

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档