OpenSSL вычислить секрет ECDH
Я уже несколько дней читаю документацию OpenSSL и не могу решить свою проблему. Из определенного интерфейса я получаю закрытый ключ ECDH и открытый ключ партнера, и я знаю, что используется кривая NID_X9_62_prime256v1 (== SECP256r1).
Я получаю эти ключи в виде RAW-байтов, что означает:
- Открытый ключ имеет длину 64 байта (X = 32 байта, Y = 32 байта)
- Закрытый ключ длиной 32 байта
Так что здесь нет X509, PCKS#8, ... что бы то ни было, только необработанные ключевые моменты.
Нет, мне нужно написать код C(99), используя OpenSSL для вычисления 32-байтового секретного секретного ECDH.
Для начала и с эталонным тестом мне предоставили пример кода на python, который проверен, чтобы дать правильный результат:
from cryptography.hazmat.primitives.asymmetric import ec
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives.serialization import \
Encoding, PrivateFormat, PublicFormat, NoEncryption, \
load_der_private_key, load_der_public_key
from binascii import hexlify
ECDH_TEST_PUBLIC_KEY = bytes([0xDC, 0x7E, 0x30, 0x1C, 0xF1, 0x6F, 0x5D, 0x5B, 0x1D, 0xA0, 0x9B, 0xF3, 0x78, 0x0D, 0x4F, 0xA1, 0x19, 0xC7, 0x8A, 0x79, 0x65, 0xF1, 0x11, 0x94, 0xF8, 0x34, 0xF1, 0x1F, 0xF3, 0x6B, 0x62, 0xE7, 0x87, 0x1C, 0x64, 0xB2, 0x5C, 0x69, 0xA3, 0x76, 0xDC, 0xE7, 0x4E, 0xCE, 0xC8, 0x30, 0xA6, 0xC0, 0xBD, 0x69, 0x0B, 0x83, 0xF7, 0x41, 0x55, 0x1F, 0xE3, 0xB0, 0x45, 0x0D, 0xD1, 0xF0, 0x94, 0xD7])
ECDH_TEST_PRIVATE_KEY = bytes([0x99, 0xDB, 0x58, 0xD8, 0x5C, 0x88, 0xF5, 0x1C, 0xE4, 0x3C, 0xA6, 0x24, 0xEC, 0xBF, 0x1C, 0x4B, 0xC1, 0x0B, 0x28, 0x96, 0xEF, 0xE9, 0xFE, 0x01, 0xCB, 0xCC, 0x2D, 0xFA, 0x67, 0x04, 0x65, 0xE2])
PRIVATE_BYTES_START = 36
PRIVATE_BYTES_END = PRIVATE_BYTES_START + 32
# Useful reading:
# https://security.stackexchange.com/questions/84327/converting-ecc-private-key-to-pkcs1-format
def raw_to_public_key(public_bytes):
assert(len(public_bytes) == 64)
# A public key in the DER format.
# We'll simply replace the actual key bytes.
DER_FMT = b'0Y0\x13\x06\x07*\x86H\xce=\x02\x01\x06\x08*\x86H\xce=\x03\x01\x07\x03B\x00\x044\xfa\xfa+E\xfa}Aj\x9e\x118N\x10\xc8r\x04\xa7e\x1d\xd2JdK\xfa\xcd\x02\xdb{\x90JA-\x0b)\xba\x05N\xa7E\x80D>\xa2\xbc"\xe3k\x89\xd1\x10*ci\x19-\xed|\xb7H\xea=L`' # NOQA
key = DER_FMT[:-64]
key += public_bytes
public_key = load_der_public_key(key, backend=default_backend())
return public_key
def raw_to_private_key(private_bytes):
assert(len(private_bytes) == 32)
# A private key in PKCS8+DER format.
# We'll simply replace the actual key bytes.
PKCS8_FMT = b'0\x81\x87\x02\x01\x000\x13\x06\x07*\x86H\xce=\x02\x01\x06\x08*\x86H\xce=\x03\x01\x07\x04m0k\x02\x01\x01\x04 \xd3e\xef\x9d\xbdc\x89\xe0.K\xc5\x84^P\r:\x9b\xfd\x038 _r`\x17\xac\xf2JJ\xff\x07\x9d\xa1D\x03B\x00\x044\xfa\xfa+E\xfa}Aj\x9e\x118N\x10\xc8r\x04\xa7e\x1d\xd2JdK\xfa\xcd\x02\xdb{\x90JA-\x0b)\xba\x05N\xa7E\x80D>\xa2\xbc"\xe3k\x89\xd1\x10*ci\x19-\xed|\xb7H\xea=L`' # NOQA
key = PKCS8_FMT[:PRIVATE_BYTES_START]
key += private_bytes
key += PKCS8_FMT[PRIVATE_BYTES_END:]
private_key = load_der_private_key(key, password=None, backend=default_backend())
return private_key
print("Testing ECDH")
public_key_peer = raw_to_public_key(ECDH_TEST_PUBLIC_KEY)
private_key = raw_to_private_key(ECDH_TEST_PRIVATE_KEY)
shared_secret = private_key.exchange(ec.ECDH(), public_key_peer)
print(hexlify(shared_secret))
Моя неработающая попытка в C:
EVP_PKEY_CTX *pctx, *kctx;
EVP_PKEY_CTX *ctx;
unsigned char *secret;
EVP_PKEY *pkey = NULL, *peerkey, *params = NULL;
/* Convert the node private key to an EVP_PKEY */
BIGNUM *privkey_bignum = BN_bin2bn((const unsigned char*) node_private, 32, NULL);
if(privkey_bignum == NULL) {
fprintf(stderr, "Could not convert raw node private key to bignum\n");
ERR_print_errors_fp(stderr);
return false;
}
EC_KEY *eckey_private = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1);
if(1 != EC_KEY_set_private_key(eckey_private, privkey_bignum)) {
fprintf(stderr, "Could not convert node private bignum to EC_KEY\n");
ERR_print_errors_fp(stderr);
return false;
}
pkey = EVP_PKEY_new();
if(pkey == NULL) {
fprintf(stderr, "Could not allocate space for a new private EVP_PKEY\n");
return false;
}
if(1 != EVP_PKEY_set1_EC_KEY(pkey, eckey_private)) {
fprintf(stderr, "Could not convert EC_KEY (private) to EVP_PKEY\n");
return false;
}
/* Convert the peer public key to an EVP_PKEY */
BIGNUM *peerpublic_bignum = BN_bin2bn((const unsigned char*) peer_public, 64, NULL);
if(peerpublic_bignum == NULL) {
fprintf(stderr, "Could not convert raw peer public key to bignum\n");
ERR_print_errors_fp(stderr);
return false;
}
EC_KEY *eckey_peerpublic = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1);
if(1 != EC_KEY_set_private_key(eckey_peerpublic, peerpublic_bignum)) {
fprintf(stderr, "Could not convert peer public bignum to EC_KEY\n");
return false;
}
peerkey = EVP_PKEY_new();
if(pkey == NULL) {
fprintf(stderr, "Could not allocate space for a new peer public EVP_PKEY\n");
return false;
}
if(1 != EVP_PKEY_set1_EC_KEY(peerkey, eckey_peerpublic)) {
fprintf(stderr, "Could not convert EC_KEY (peer public) to EVP_PKEY\n");
return false;
}
/* NB: assumes pkey, peerkey have been already set up */
/* Create the context for parameter generation */
if(NULL == (pctx = EVP_PKEY_CTX_new_id(EVP_PKEY_EC, NULL))) {
fprintf(stderr, "Could not create the ECDH key generation context\n");
return false;
}
/* Initialize the parameter generation */
if(1 != EVP_PKEY_paramgen_init(pctx)) {
fprintf(stderr, "Could not initialize the key parameter generation\n");
return false;
}
/* We're going to use the ANSI X9.62 Prime 256v1 (SECP256r1) curve */
if(1 != EVP_PKEY_CTX_set_ec_paramgen_curve_nid(pctx, NID_X9_62_prime256v1)) {
fprintf(stderr, "Could not select the ANSI X9.62 Prime 256v1 curve\n");
return false;
}
/* Create the parameter object parameters */
if (!EVP_PKEY_paramgen(pctx, ¶ms)) {
fprintf(stderr, "Could not create the parameter object parameters\n");
return false;
}
/* Create the context for the key generation */
if(NULL == (kctx = EVP_PKEY_CTX_new(params, NULL))) {
fprintf(stderr, "Could not create the context for the key generation\n");
return false;
}
/* Create the context for the shared secret derivation */
if(NULL == (ctx = EVP_PKEY_CTX_new(pkey, NULL))) {
fprintf(stderr, "Could not create the context for the shared secret derivation\n");
return false;
}
/* Initialize */
if(1 != EVP_PKEY_derive_init(ctx)) {
fprintf(stderr, "Could not initialize the key derivation\n");
return false;
}
/* Provide the peer public key */
if(1 != EVP_PKEY_derive_set_peer(ctx, peerkey)) {
fprintf(stderr, "Could not set the peer public key into the derivation procedure\n");
return false;
}
/* Determine buffer length for shared secret */
size_t secret_len = 0;
if(1 != EVP_PKEY_derive(ctx, NULL, &secret_len))
{
fprintf(stderr, "Could not determine the length of the shared secret\n");
return false;
}
printf("Shared secret size: %d\n", secret_len);
/* Create the buffer */
if(NULL == (secret = OPENSSL_malloc(secret_len))) {
fprintf(stderr, "Could not create the secret buffer\n");
return false;
}
/* Derive the shared secret */
int retvalue = EVP_PKEY_derive(ctx, secret, &secret_len);
if(1 != retvalue) {
fprintf(stderr, "Could not derive the ECDH shared secret: %d\n", retvalue);
ERR_print_errors_fp(stderr);
return false;
}
EVP_PKEY_CTX_free(ctx);
EVP_PKEY_free(peerkey);
EVP_PKEY_free(pkey);
EVP_PKEY_CTX_free(kctx);
EVP_PKEY_free(params);
EVP_PKEY_CTX_free(pctx);
printf("\tSecret: ");
for(int i = 0; i < 32; ++i) {
printf("%02x", secret[i]);
}
printf("\n");
Мое чувство подсказывает мне, что проблема заключается в преобразовании необработанных открытых и закрытых ключей во что-то, что понимает OpenSSL.
С уважением, Даан