Экспорт 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.
Вы можете создать то, что вы хотите из командной строки, вот так:
- Создать секретный ключ EC: openssl ecparam -name prime256v1 -genkey -noout -out key.pem
- Дополнительный открытый ключ в формате DER(ASN1): openssl ec -in key.pem -pubout -outform der -out public.cer
- Конвертировать в base64 openssl base64 -in .\ Public.cer
Если вы возьмете этот вывод и вставите его в ссылку декодера ASN.1, он будет работать нормально.
Теперь, чтобы превратить это в код, у вас есть генерация ключа EC, но вам нужны следующие шаги:
- Создать открытый ключ в формате ASN1
- Преобразовать его в 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, то он будет работать с примером приложения.