Экспорт EC_POINT с использованием point2hex в ASN1.DER и воссоздание с Java в качестве X.509

Я генерирую пару ключей ECDSA Prime256, используя OpenSSL с C++, и пытаюсь импортировать шестнадцатеричную версию открытого ключа, используя Java. Я передаю байтовый массив, полученный из C++, следующей функции в java, которая ожидает, что байтовый массив будет в кодированном формате X.509.

public static PublicKey getPublicKey(byte[] pk) throws NoSuchAlgorithmException, InvalidKeySpecException {
    EncodedKeySpec publicKeySpec = new X509EncodedKeySpec(pk);
    KeyFactory kf = KeyFactory.getInstance(Constant.KEY_FACTORY_TYPE);
    PublicKey pub = kf.generatePublic(publicKeySpec);
    return pub;
}

Я создаю пару ключей эллиптической кривой, используя следующую функцию, которая перенастраивает EC_KEY*

EC_KEY* generate_keypair() {
    EC_KEY *eckey = EC_KEY_new();
    EC_GROUP *ecgroup = EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1);
    EC_KEY_set_group(eckey, ecgroup);
    EC_KEY_set_asn1_flag(eckey, OPENSSL_EC_NAMED_CURVE);
    int kpGenerationStatus = EC_KEY_generate_key(eckey);
    if (kpGenerationStatus) {
        return eckey;
    }
    return nullptr;
}

Учитывая пару ключей, возвращенную вышеописанной функцией, я хочу экспортировать открытый ключ в формат ASN1.DER, который можно импортировать с помощью метода java, описанного выше.

Я конвертирую открытый ключ, который имеет тип EC_POINT* в его шестнадцатеричной форме, используя EC_POINT_point2hex() сделав следующее:

EC_GROUP *ecgroup = EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1);
EC_KEY *keypair = generate_keypair();
char *result = NULL;
BN_CTX *ctx;
ctx = BN_CTX_new();
const EC_POINT *pub = EC_KEY_get0_public_key(keypair);
result = EC_POINT_point2hex(ecgroup, pub, POINT_CONVERSION_UNCOMPRESSED, ctx);
printf("%s\n", result);

Которые возвращают следующее:04F588CD1D7103A993D47E53D58C3F40BE8F570604CF2EA01A7657C1423EB19C51BC379F0BEE1FAA60BB9A07DE73EA9BEF7709C1C6429D4051B44F73A458FFB80D

Когда я проверяю это с помощью декодера ASN.1, я вижу сообщение, которое говорит Length over 48 bits not supported at position 1 и пытаясь импортировать его с помощью метода Java, я получаю сообщение об ошибке следующим образом:

java.security.spec.InvalidKeySpecException: java.security.InvalidKeyException: IOException: DerInputStream.getLength(): Should use short form for length

Есть ли что-то, чего мне не хватает при экспорте открытого ключа из EC_POINT* в закодированную шестнадцатеричную строку X.509, которую я могу импортировать для проверки любых подписей?

0 ответов

Вы идете в неправильном направлении, поскольку вы хотите ASN1 base64 значение.

EC_POINT_point2hex преобразует значение внутреннего открытого ключа в шестнадцатеричное. Это не в формате ASN1.

Вы можете создать то, что вы хотите из командной строки, вот так:

  1. Создать секретный ключ EC: openssl ecparam -name prime256v1 -genkey -noout -out key.pem
  2. Дополнительный открытый ключ в формате DER(ASN1): openssl ec -in key.pem -pubout -outform der -out public.cer
  3. Конвертировать в base64 openssl base64 -in .\ Public.cer

Если вы возьмете этот вывод и вставите его в ссылку декодера ASN.1, он будет работать нормально.

Теперь, чтобы превратить это в код, у вас есть генерация ключа EC, но вам нужны следующие шаги:

  1. Создать открытый ключ в формате ASN1
  2. Преобразовать его в base64

Для генерации открытого ключа в формате ASN1 вы хотите использовать набор методов i2d_EC_PUBKEY, а затем преобразовать в base64 с помощью фильтра BIO_f_base64.

Итак, вот примерная проблема: когда я копирую вывод в ссылку на декодер ASN.1, он работает нормально.

#include <openssl/bio.h>
#include <openssl/ec.h>
#include <openssl/evp.h>
#include <openssl/x509.h>

EC_KEY* generate_keypair() {
    EC_KEY *eckey = EC_KEY_new();
    EC_GROUP *ecgroup = EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1);
    EC_KEY_set_group(eckey, ecgroup);
    EC_KEY_set_asn1_flag(eckey, OPENSSL_EC_NAMED_CURVE);
    int kpGenerationStatus = EC_KEY_generate_key(eckey);
    if (kpGenerationStatus) {
        return eckey;
    }
    return nullptr;
}

int main()
{
    EC_GROUP *ecgroup = EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1);
    EC_KEY *keypair = generate_keypair();
    BIO* out = BIO_new(BIO_s_mem());
    BIO* b64 = BIO_new(BIO_f_base64());
    BIO_push(b64, out);

    i2d_EC_PUBKEY_bio(b64, keypair);

    BIO_flush(b64);


    // do what you want this the output in out memory BIO

    char* p;
    long length = BIO_get_mem_data(out, &p);

    // ensure null terminated but copying the buffer into a string to output...
    puts(std::string(p, length).c_str());

    BIO_free_all(out);
}

Я не могу выполнить на стороне Java, но если он работает со сгенерированной вручную строкой base64 openssl, то он будет работать с примером приложения.

Другие вопросы по тегам