Convert a plain public key to PEM

妖精的绣舞 提交于 2019-12-06 07:20:13

Using the openssl utility the command you can use is:

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

To reproduce this with code you need to use these main openssl api's

An openssl example, turned into C++ code around the C openssl API, would be:

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

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.

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

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) 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 .

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!