OpenSSL: прочитайте EC-ключ, затем запишите его снова, и он будет другим - PullRequest
0 голосов
/ 23 мая 2018

Я написал оболочку для OpenSSL, которая поддерживает ECC.Я пытаюсь прочитать закрытый ключ, сгенерированный с помощью

openssl ecparam -name secp384r1 -genkey -noout -out privkey.pem

, и сравнить его с тем, что OpenSSL выдаст после считывания ключа в EVP_PKEY и EC_KEY и печати его снова в строку,Результаты после прочтения не совпадают.

Короче говоря:

  1. Считайте ключ
  2. Сохраните его в EVP_PKEY
  3. Напишите егоснова

И результаты не совпадают.Моя программа довольно большая, поэтому я создал MCVE, который демонстрирует проблему .

Я подозреваю, что проблема возникает из-за того, что я читаю в EC_KEY, а затем пишу из EVP_PKEY, что является общим.Я предполагаю здесь, потому что вход говорит, что это EC, но выход не говорит это.Я не уверен, как решить эту проблему, потому что я не вижу способа записи непосредственно из EC_KEY в файл (био-объект).Является ли моя оценка правильной?

Пожалуйста, сообщите.


РЕДАКТИРОВАТЬ: меня попросили поместить весь код здесь в комментариях, так что вы идете:

#include <iostream>

#include <openssl/bio.h>
#include <openssl/err.h>
#include <openssl/ec.h>
#include <openssl/pem.h>

EC_KEY* ecKey = nullptr;
EVP_PKEY* pkey = nullptr;

void setPrivateKeyFromPEM(const std::string& pemkey)
{
    pkey = EVP_PKEY_new();

    BIO* bio = BIO_new(BIO_s_mem());

    int bio_write_ret = BIO_write(
        bio, static_cast<const char*>(pemkey.c_str()), pemkey.size());
    if (bio_write_ret <= 0) {
        throw std::runtime_error("error1");
    }

    if (!PEM_read_bio_PrivateKey(bio, &pkey, NULL, NULL)) {
        throw std::runtime_error("error1.5");
    }

    EC_KEY* eckey_local = EVP_PKEY_get1_EC_KEY(pkey);

    if (!eckey_local) {
        throw std::runtime_error("error2");
    } else {
        ecKey = eckey_local;
        EC_KEY_set_asn1_flag(ecKey, OPENSSL_EC_NAMED_CURVE);
    }
}

std::string getPrivateKeyAsPEM()
{
    if (!pkey) {
        throw std::runtime_error("error3");
    }

    BIO* outbio = BIO_new(BIO_s_mem());

    if (!PEM_write_bio_PrivateKey(outbio, pkey, NULL, NULL, 0, 0,
                                  NULL)) {
        throw std::runtime_error("error4");
    }

    std::string keyStr;
    int         priKeyLen = BIO_pending(outbio);
    keyStr.resize(priKeyLen);
    BIO_read(outbio, (void*)&(keyStr.front()), priKeyLen);
    return keyStr;
}

int main()
{
    std::string expectedPrivKey =
        "-----BEGIN EC PRIVATE KEY-----\n"
        "MIGkAgEBBDBNK0jwKqqf8zkM+Z2l++9r8bzdTS/XCoB4N1J07dPxpByyJyGbhvIy\n"
        "1kLvY2gIvlmgBwYFK4EEACKhZANiAAQvPxAK2RhvH/k5inDa9oMxUZPvvb9fq8G3\n"
        "9dKW1tS+ywhejnKeu/48HXAXgx2g6qMJjEPpcTy/DaYm12r3GTaRzOBQmxSItStk\n"
        "lpQg5vf23Fc9fFrQ9AnQKrb1dgTkoxQ=\n"
        "-----END EC PRIVATE KEY-----\n";

    setPrivateKeyFromPEM(expectedPrivKey);
    // compare priv key
    {
        std::string privKeyRead = getPrivateKeyAsPEM();
        std::cout << privKeyRead << std::endl;
        std::cout<<expectedPrivKey<<std::endl;
    }

    return 0;
}

Ответы [ 2 ]

0 голосов
/ 23 мая 2018

PEM_write_bio_ECPrivateKey также доступно в OpenSSL 1.0.2, отсутствует только документация.

Сохраненный ключ тот же, разница только в кодировке.

Тег -----BEGIN PRIVATE KEY----- обозначает PEM-кодированный формат ASN.1.

Тег -----BEGIN EC PRIVATE KEY----- обозначает PEM-кодированный ключ ANSI X9.62.

Сравните: ключ 1 против ключ 2 .Обратите внимание, что key2 не содержит OID типа ключа, сам ключ идентичен.

Чтобы записать формат ключа EC, просто используйте это:

    if (!PEM_write_bio_ECPrivateKey(outbio, ecKey, NULL, NULL, 0, 0, NULL)) {

coliru-demo

0 голосов
/ 23 мая 2018

Хотя выходные данные не совпадают точно, они фактически представляют одну и ту же клавишу.

Ваш код выводит следующее:

-----BEGIN PRIVATE KEY-----
MIG2AgEAMBAGByqGSM49AgEGBSuBBAAiBIGeMIGbAgEBBDBNK0jwKqqf8zkM+Z2l
++9r8bzdTS/XCoB4N1J07dPxpByyJyGbhvIy1kLvY2gIvlmhZANiAAQvPxAK2Rhv
H/k5inDa9oMxUZPvvb9fq8G39dKW1tS+ywhejnKeu/48HXAXgx2g6qMJjEPpcTy/
DaYm12r3GTaRzOBQmxSItStklpQg5vf23Fc9fFrQ9AnQKrb1dgTkoxQ=
-----END PRIVATE KEY-----

-----BEGIN EC PRIVATE KEY-----
MIGkAgEBBDBNK0jwKqqf8zkM+Z2l++9r8bzdTS/XCoB4N1J07dPxpByyJyGbhvIy
1kLvY2gIvlmgBwYFK4EEACKhZANiAAQvPxAK2RhvH/k5inDa9oMxUZPvvb9fq8G3
9dKW1tS+ywhejnKeu/48HXAXgx2g6qMJjEPpcTy/DaYm12r3GTaRzOBQmxSItStk
lpQg5vf23Fc9fFrQ9AnQKrb1dgTkoxQ=
-----END EC PRIVATE KEY-----

Если вы внимательно посмотрите на первую напечатанную клавишу,который импортируется / экспортируется вашим кодом, он помечен как «BEGIN PRIVATE KEY» вместо «BEGIN EC PRIVATE KEY».Он также начинается с MIG2AgEAMBAGByqGSM49AgEGBSuBBAAiBIGe, но после этого он идентичен исходному ключу.Фактически, если вы поместите оба файла PEM в файлы и запустите openssl ec -in {filename} -text, оба будут выводить одно и то же:

read EC key
Private-Key: (384 bit)
priv:
    4d:2b:48:f0:2a:aa:9f:f3:39:0c:f9:9d:a5:fb:ef:
    6b:f1:bc:dd:4d:2f:d7:0a:80:78:37:52:74:ed:d3:
    f1:a4:1c:b2:27:21:9b:86:f2:32:d6:42:ef:63:68:
    08:be:59
pub: 
    04:2f:3f:10:0a:d9:18:6f:1f:f9:39:8a:70:da:f6:
    83:31:51:93:ef:bd:bf:5f:ab:c1:b7:f5:d2:96:d6:
    d4:be:cb:08:5e:8e:72:9e:bb:fe:3c:1d:70:17:83:
    1d:a0:ea:a3:09:8c:43:e9:71:3c:bf:0d:a6:26:d7:
    6a:f7:19:36:91:cc:e0:50:9b:14:88:b5:2b:64:96:
    94:20:e6:f7:f6:dc:57:3d:7c:5a:d0:f4:09:d0:2a:
    b6:f5:76:04:e4:a3:14
ASN1 OID: secp384r1
NIST CURVE: P-384
writing EC key
-----BEGIN EC PRIVATE KEY-----
MIGkAgEBBDBNK0jwKqqf8zkM+Z2l++9r8bzdTS/XCoB4N1J07dPxpByyJyGbhvIy
1kLvY2gIvlmgBwYFK4EEACKhZANiAAQvPxAK2RhvH/k5inDa9oMxUZPvvb9fq8G3
9dKW1tS+ywhejnKeu/48HXAXgx2g6qMJjEPpcTy/DaYm12r3GTaRzOBQmxSItStk
lpQg5vf23Fc9fFrQ9AnQKrb1dgTkoxQ=
-----END EC PRIVATE KEY-----

Дополнительная часть в начале - это метаданные от экспорта ключа EVP_PKEY, в котором указано, чтоключ - это ключ EC.

Если бы вы вместо этого экспортировали EC_KEY напрямую, используя PEM_write_bio_ECPrivateKey(outbio, ecKey, NULL, NULL, 0, 0, NULL), результат был бы точно таким же.

...