Секреты ECDH, генерируемые Java-API BouncyCastle и OpenSSL, различны
Я пытаюсь использовать криптографию с эллиптической кривой. Мне нужны две реализации одной и той же вещи, одна на Java и одна на C. Я тестирую их, используя две пары ключей, которые были сгенерированы с использованием кривой secp256k1. Когда я генерирую производный секрет в Java, я всегда получаю число, отличное от того, что я получаю от OpenSSL.
Java-код:
/* privateKey and peerPublicKey are generated with the following parameters */
ECParameterSpec paramSpec = ECNamedCurveTable.getParameterSpec("secp256k1");
/* ... */
Provider BC = new BouncyCastleProvider();
KeyAgreement agr = KeyAgreement.getInstance("ECDH", BC);
agr.init(privateKey);
agr.doPhase(peerPublicKey, true);
byte[] secret = agr.generateSecret();
Код C
/* pkey and peerkey are generated using EC_KEY_new_by_curve_name(NID_secp256k1) */
/* and than wrapped in an EVP_PKEY */
EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new(pkey, NULL);
uint8_t *secret = NULL;
size_t secret_len;
EVP_PKEY_derive_init(ctx);
EVP_PKEY_derive_set_peer(ctx, peerkey);
EVP_PKEY_derive(ctx, NULL, &secret_len);
secret = malloc(secret_len);
EVP_PKEY_derive(ctx, secret, &secret_len);
Я уверен, что ключи действительны и что они одинаковы как в C, так и в Java-коде, но я не понимаю, почему производный секрет отличается. Я что-то пропустил?
Спасибо
1 ответ
EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new(pkey, NULL);
uint8_t *secret = NULL;
size_t secret_len;
EVP_PKEY_derive_init(ctx);
EVP_PKEY_derive_set_peer(ctx, peerkey);
EVP_PKEY_derive(ctx, NULL, &secret_len);
secret = malloc(secret_len);
EVP_PKEY_derive(ctx, secret, &secret_len);
Этот код выглядит как пропущенный в нескольких шагах. Например, EVP_PKEY_paramgen_init
нет
В OpenSSL вики есть пример на Diffie-Hellman с эллиптической кривой. Я собираюсь скопировать / вставить его ниже, чтобы избежать ответа только на ссылку, но я верю, что это работа Мэтта Касвелла.
EVP_PKEY_CTX *pctx, *kctx;
EVP_PKEY_CTX *ctx;
unsigned char *secret;
EVP_PKEY *pkey = NULL, *peerkey, *params = NULL;
/* Create the context for parameter generation */
if(NULL == (pctx = EVP_PKEY_CTX_new_id(EVP_PKEY_EC, NULL))) handleErrors();
/* Initialise the parameter generation */
if(1 != EVP_PKEY_paramgen_init(pctx)) handleErrors();
/* We're going to use the ANSI X9.62 Prime 256v1 curve */
if(1 != EVP_PKEY_CTX_set_ec_paramgen_curve_nid(pctx, NID_X9_62_prime256v1)) handleErrors();
/* Create the parameter object params */
if (!EVP_PKEY_paramgen(pctx, ¶ms)) handleErrors();
/* Create the context for the key generation */
if(NULL == (kctx = EVP_PKEY_CTX_new(params, NULL))) handleErrors();
/* Generate the key */
if(1 != EVP_PKEY_keygen_init(kctx)) handleErrors();
if (1 != EVP_PKEY_keygen(kctx, &pkey)) handleErrors();
/* Get the peer's public key, and provide the peer with our public key -
* how this is done will be specific to your circumstances */
peerkey = get_peerkey(pkey);
/* Create the context for the shared secret derivation */
if(NULL == (ctx = EVP_PKEY_CTX_new(pkey, NULL))) handleErrors();
/* Initialise */
if(1 != EVP_PKEY_derive_init(ctx)) handleErrors();
/* Provide the peer public key */
if(1 != EVP_PKEY_derive_set_peer(ctx, peerkey)) handleErrors();
/* Determine buffer length for shared secret */
if(1 != EVP_PKEY_derive(ctx, NULL, secret_len)) handleErrors();
/* Create the buffer */
if(NULL == (secret = OPENSSL_malloc(*secret_len))) handleErrors();
/* Derive the shared secret */
if(1 != (EVP_PKEY_derive(ctx, secret, secret_len))) handleErrors();
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);
/* Never use a derived secret directly. Typically it is passed
* through some hash function to produce a key */
return secret;
Когда я генерирую производный секрет в Java, я всегда получаю число, отличное от того, что я получаю от OpenSSL.
Каждый запуск протокола будет давать разные результаты. Это потому, что каждая сторона выбирает случайное значение для каждого прогона протокола. Это a
в g^a
является случайным и отличается для каждого прогона, поэтому открытый ключ A = g^a
отличается для каждого запуска.
Если все работает правильно, вы никогда не увидите, чтобы стороны использовали одинаковые значения или одну сторону для повторного использования прошлого значения. Независимые казни никогда не приведут к одному и тому же результату. Не имеет значения, является ли его OpenSSL, OpenSSL, OpenSSL, Java или Java, Java. Они всегда будут давать разные результаты.