Преобразовать простой открытый ключ в PEM - PullRequest
2 голосов
/ 20 мая 2019

Я сгенерировал пару ключей EC с использованием Prime-256v1 из доверенного приложения и экспортировал открытый ключ в обычную ОС.Размер ключа составляет 65 байт.Открытый ключ в простом формате (только шестнадцатеричный ключ).

Экспортированный открытый ключ необходимо передать библиотеке (третьей стороне).Библиотека ожидает открытый ключ в формате PEM.

После поиска в течение некоторого времени мое понимание заключается в том, чтобы сначала преобразовать обычный ключ в формат DER, а затем преобразовать результат в PEM.Но я не смог найти никаких API для преобразования из простого ключа в DER или PEM.

Нашел этот 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.

Заранее спасибо

Ответы [ 2 ]

1 голос
/ 28 мая 2019

Значение, которое вы указали в комментарии 4bb5f0c58cc71806ec4d228b730dd252947e679cce05f71d434787fe228f14c799cf8965780bb308aa722ac179bfa5fd57592a72cbdcfe89ab61ad5d77251186d - неправильная длина. Это 129 шестнадцатеричных цифр, также называемых полубайтами, но кодированная точка для prime256v1 (также известный как secp256r1 или P-256) должна быть либо 65 октетов, начинающихся с 04 (несжатых), либо 33 октетов, начинающихся с 02 или 03 (сжатых). Когда шестнадцатеричная строка (или десятичная или восьмеричная в этом отношении) представляет целое число , вы можете удалить или добавить начальные нули без изменения числа, но точка EC будет , а не целым числом.

Вы говорите, что ваш код обеспечивает 65 байтов, что является правильным для несжатого. Используйте это.

В отличие от большинства других PK-алгоритмов, OpenSSL не имеет специфичного для алгоритма формата DER (и, следовательно, PEM) для ECC, поэтому все, что точно описывается как получение открытого ключа PEM (а не - сертификат, который является обычный способ передачи публичного ключа) должен использовать формат X.509 / PKIX SubjectPublicKeyInfo, который OpenSSL называет PUBKEY (как показано в ответе Шейна) и отображает из или в EVP_PKEY структуру в программе. Чтобы построить это из необработанной общедоступной точки, вы должны сначала объединить ее с идентификатором кривой, чтобы сформировать реальный открытый ключ EC, а затем идентифицировать его как EC, чтобы сформировать универсальный открытый ключ, и записать его. OpenSSL может выполнять ввод / вывод (включая PEM) в память и из нее, используя BIO типа mem, без каких-либо файлов. (И сделать это твердо вопрос программирования, а не вопрос командной строки, который действительно был бы оффтопом и принадлежал бы другому стеку, хотя многие все равно задаются здесь.)

/* 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 является фиксированным размером для данной кривой, структура SPKI в кодировке DER для данной кривой фактически состоит из фиксированного заголовка, за которым следует значение точки, поэтому вместо этого можно объединить с это фиксированный заголовок и сделать общее преобразование PEM: выходной штрих-начало строки, выходной base64 с разрывами строки, выходной штрих-конец строки. Несмотря на то, что не сложно определить заголовок, если известно, как работает ASN.1, можно создать кодировку SPKI для фиктивного ключа, например, с помощью openssl ecparam -genkey -name prime256v1 -outform der и удалите последние 65 байтов (или 33, если сжаты с использованием -conv_form). Сравните с вариантами Java на Загрузка необработанного открытого ключа ECDSA длиной 64 байта в Java .

1 голос
/ 20 мая 2019

Используя утилиту openssl, вы можете использовать команду:

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

Чтобы воспроизвести это с кодом, вам нужно использовать эти основные API OpenSSL

Пример openssl, превращенный в код C ++ вокруг API openssl 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;
}
...