将普通公钥转换为 PEM [英] Convert a plain public key to PEM

查看:473
本文介绍了将普通公钥转换为 PEM的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I have generated an EC key pair using Prime-256v1 from a trusted application and export the public key to Normal OS. Key size is 65 bytes. The public key is in plain format (only key hex).

导出的公钥需要提供给图书馆(第三方).图书馆需要 PEM 格式的公钥.

The exported public key needs to be given to a library (third party). The library expects the public key in PEM format.

找了一段时间,我的理解是先把plain key转成DER格式,再把结果转成PEM.但是我一直没有找到任何用于从普通密钥转换为 DER 或 PEM 的 API.

After searching for some time, my understanding is first convert from plain key to DER format, and then convert the result to PEM. But I have not been able to find any APIs for the conversion from plain key to DER or PEM.

发现这个API PEM_ASN1_write((i2d_of_void*)i2d_PUBKEY,PEM_STRING_PUBLIC,outfile,ctx->cert->key->public_key,NULL,NULL,0,NULL,NULL);从文件指针转换而来.但我无法进行文件操作,因为无法进行文件存储.我正在缓冲区中获取公钥.

Found this API which PEM_ASN1_write((i2d_of_void*)i2d_PUBKEY,PEM_STRING_PUBLIC,outfile,ctx->cert->key->public_key,NULL,NULL,0,NULL,NULL); which convert from a file pointer. But I am not able to do file operations as not file storage possible. I am getting public key in a buffer.

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

I am doing this in C program, if any sample code or API's to convert plain hex key to PEM.

提前致谢

推荐答案

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

The value you provided in a comment 4bb5f0c58cc71806ec4d228b730dd252947e679cce05f71d434787fe228f14c799cf8965780bb308aa722ac179bfa5fd57592a72cbdcfe89ab61ad5d77251186d is the wrong length. It is 129 hex digits, aka nibbles, but an encoded point for prime256v1 (aka secp256r1 or P-256) must either be 65 octets beginning with 04 (uncompressed) or 33 octets beginning with 02 or 03 (compressed). When a hex string (or decimal or octal for that matter) represents an integer, you can remove or add leading zeros without changing the number, but an EC point is not an integer.

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

You say your code is providing 65 bytes which is correct for uncompressed. Use that.

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

Unlike most other PK algorithms, OpenSSL does not have an algorithm-specific DER (and thus PEM) format for ECC, so anything accurately described as wanting a PEM publickey (and not a certificate, which is the conventional way of transmitting a publickey) must be using the X.509/PKIX SubjectPublicKeyInfo format, which OpenSSL names PUBKEY (as shown in Shane's answer) and maps from or to an EVP_PKEY structure in the program. To construct this from the raw public point, you must first combine it with the curve identification to form an actual EC publickey, and then identify that as EC to form a generic publickey, and write it. OpenSSL can do I/O (including PEM) to and from memory using a BIO of type 'mem', without any file(s). (And making this firmly a programming question and not a commandline question, which would really be offtopic and belong in another Stack, although many are asked here anyway.)

/* 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;
}

(ADDED) 或者,由于 X9.62 点编码是给定曲线的固定大小,给定曲线的 DER 编码 SPKI 结构实际上由一个固定标头和点值组成,因此您可以改为连接固定标题并进行通用 PEM 转换:输出破折号-BEGIN 行,输出带有换行符的 base64,输出破折号-END 行.尽管如果知道 ASN.1 的工作原理,计算标头并不难,但一种快捷方式是为虚拟密钥生成 SPKI 编码,例如openssl ecparam -genkey -name prime256v1 -outform der 并删除最后 65 个字节(如果使用 -conv_form 压缩,则删除 33 个字节).与 Loading raw 64-byte long 中的 Java 变体进行比较Java 中的 ECDSA 公钥.

(ADDED) Alternatively, since X9.62 point encoding is fixed size for a given curve, the DER-encoded SPKI structure for a given curve actually consists of a fixed header followed by the point value, so you could instead concatenate with that fixed header and do generic PEM conversion: output dashes-BEGIN line, output base64 with line breaks, output dashes-END line. Although it is not hard to work out the header if one knows how ASN.1 works, a shortcut is to generate the SPKI encoding for a dummy key with e.g. openssl ecparam -genkey -name prime256v1 -outform der and remove the last 65 bytes (or 33 if compressed using -conv_form). Compare to the Java variants at Loading raw 64-byte long ECDSA public key in Java .

这篇关于将普通公钥转换为 PEM的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆