#c #openssl #elliptic-curve
#c #openssl #эллиптическая кривая
Вопрос:
У меня есть следующая программа. Он успешно получает необработанные 32-байтовые данные закрытого ключа EC, а затем создает из них EC_KEY. Но i2d_ECPrivateKey не может указать размер закрытого ключа, закодированного в DER, поскольку происходит сбой. Кто-нибудь знает, почему и как это исправить?
#include "CBWIF.h"
#include <openssl/ssl.h>
int main(int argc, char * argv[]) {
CBWIF wif;
if (argc != 2)
return EXIT_FAILURE;
// Decode WIF string
CBByteArray str;
CBInitByteArrayFromString(amp;str, argv[1], false);
CBInitWIFFromString(amp;wif, amp;str, false);
CBDestroyByteArray(amp;str);
// Get key
uint8_t key[32];
CBWIFGetPrivateKey(amp;wif, key);
CBDestroyWIF(amp;wif);
// Create OpenSSL key
EC_KEY * eckey = EC_KEY_new_by_curve_name(NID_secp256k1);
BIGNUM * bn = BN_bin2bn(key, CB_PRIVKEY_SIZE, NULL);
if (!EC_KEY_set_private_key(eckey, bn))
return EXIT_FAILURE;
// Convert key to DER format
int len = i2d_ECPrivateKey(eckey, NULL); // <-- CRASH HERE
unsigned char derkey[len];
i2d_ECPrivateKey(eckey, (unsigned char **)amp;derkey);
EC_KEY_free(eckey);
// Encode DER key as hex
char out[len*2 1];
CBBytesToString(derkey, 0, len, out, false);
// Print to stdout
puts(out);
return EXIT_SUCCESS;
}
CB_PRIVKEY_SIZE
равно 32. Я проверил, что ключевые данные из CBWIFGetPrivateKey
верны. Программа завершает работу со следующей трассировкой стека:
#0 0x00007ffff766cb03 in EC_POINT_point2oct () from /usr/lib/x86_64-linux-gnu/libcrypto.so.1.0.0
#1 0x00007ffff7658124 in i2d_ECPrivateKey () from /usr/lib/x86_64-linux-gnu/libcrypto.so.1.0.0
#2 0x0000000000400bf6 in main (argc=2, argv=0x7fffffffe038) at examples/WIF2DER.c:46
Комментарии:
1. Я только что наткнулся на это из проекта Vahalla от OpenBSD: Избегайте разыменования NULL в
i2d_ECPrivateKey()
, когда в EC_KEY отсутствует элемент открытого ключа .
Ответ №1:
Причина в том, что вы не можете запустить i2d_ECPrivateKey без установки открытого ключа, вероятно, из-за ошибки в OpenSSL. Я сгенерировал открытый ключ и решил несколько других проблем, и теперь программа работает:
#include "CBWIF.h"
#include <openssl/ssl.h>
int main(int argc, char * argv[]) {
CBWIF wif;
if (argc != 2)
return EXIT_FAILURE;
// Decode WIF string
CBByteArray str;
CBInitByteArrayFromString(amp;str, argv[1], false);
CBInitWIFFromString(amp;wif, amp;str, false);
CBDestroyByteArray(amp;str);
// Get key
uint8_t key[32];
CBWIFGetPrivateKey(amp;wif, key);
CBDestroyWIF(amp;wif);
// Create OpenSSL key
EC_KEY * eckey = EC_KEY_new_by_curve_name(NID_secp256k1);
BIGNUM * bn = BN_bin2bn(key, CB_PRIVKEY_SIZE, NULL);
if (!EC_KEY_set_private_key(eckey, bn))
return EXIT_FAILURE;
// Create public key as OpenSSL cannot do this easily
EC_GROUP * group = EC_GROUP_new_by_curve_name(NID_secp256k1);
EC_POINT * point = EC_POINT_new(group);
BN_CTX * ctx = BN_CTX_new();
EC_POINT_mul(group, point, bn, NULL, NULL, ctx);
BN_CTX_free(ctx);
EC_GROUP_free(group);
BN_free(bn);
if (!EC_KEY_set_public_key(eckey, point))
return EXIT_FAILURE;
EC_POINT_free(point);
// Check the key
if (!EC_KEY_check_key(eckey))
return EXIT_FAILURE;
// Convert key to DER format
int len = i2d_ECPrivateKey(eckey, NULL);
unsigned char derkey[len];
unsigned char * derkeyPtr = derkey;
i2d_ECPrivateKey(eckey, amp;derkeyPtr);
// Freeing the EC_KEY here crashes for some reason???
// Encode DER key as hex
char out[len*2 1];
CBBytesToString(derkey, 0, len, out, false);
// Print to stdout
puts(out);
return EXIT_SUCCESS;
}
Ответ №2:
Вот как OpenSSL использует это в <openssl src/crypto/ec/ec_ameth.c
. Все другие аналогичные варианты использования в библиотеке используют i2d_ECPrivateKey_bio
. Кроме того, вы могли бы быстро взглянуть на то, как OPENSSL_EC_NAMED_CURVE
флаг используется с V_ASN1_OBJECT
.
unsigned char *ep, *p;
int eplen, ptype;
unsigned int tmp_flags, old_flags;
...
old_flags = EC_KEY_get_enc_flags(ec_key);
tmp_flags = old_flags | EC_PKEY_NO_PARAMETERS;
...
eplen = i2d_ECPrivateKey(ec_key, NULL);
if (!eplen)
{
EC_KEY_set_enc_flags(ec_key, old_flags);
ECerr(EC_F_ECKEY_PRIV_ENCODE, ERR_R_EC_LIB);
return 0;
}
ep = (unsigned char *) OPENSSL_malloc(eplen);
if (!ep)
{
EC_KEY_set_enc_flags(ec_key, old_flags);
ECerr(EC_F_ECKEY_PRIV_ENCODE, ERR_R_MALLOC_FAILURE);
return 0;
}
p = ep;
if (!i2d_ECPrivateKey(ec_key, amp;p))
{
EC_KEY_set_enc_flags(ec_key, old_flags);
OPENSSL_free(ep);
ECerr(EC_F_ECKEY_PRIV_ENCODE, ERR_R_EC_LIB);
return 0;
}
...
Также обратите внимание на предотвращение разыменования NULL в i2d_ECPrivateKey()
случае, когда в EC_KEY отсутствует элемент открытого ключа из проекта LibreSSL OpenBSD.