0

I'm trying to make use of elliptic curve crypto. I need two implementations of the same thing, one in Java and one in C. I'm testing them using two key pairs which were generated using the curve secp256k1. When I generate the derived secret in Java I always get a different number from what I get from OpenSSL.

Java code:

/* 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 code

/* 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);

I'm sure that the keys are valid and that they are the same both in C and in Java code, but I don't understand why the derived secret is different. Am I missing something?

Thanks

maff
  • 855
  • 1
  • 9
  • 10
  • "When I generate the derived secret in Java I always get a different number from what I get from OpenSSL." - what, precisely, do you mean? Does each execution of the protocol result in different secrets? Or is it the case that an execution of the protocol between an OpenSSL client and BC client does not arrive at a shared secret? – jww Mar 03 '14 at 23:42

1 Answers1

2
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);

This code looks like its missing a few steps. For example, EVP_PKEY_paramgen_init is not present.

The OpenSSL wiki has an example at Elliptic Curve Diffie-Hellman. I'm going to copy/paste it below to avoid the link-only answer, but I believe its the work of Matt Caswell.

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, &params)) 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;

When I generate the derived secret in Java I always get a different number from what I get from OpenSSL.

Each run of the protocol will produce different results. That's because each party picks a random value for each run of the protocol. That is, the a in g^a is random and different for each run, so the public key A = g^a is different for each run.

If everything is working correctly, you'll never see the parties use the same values, or one party to reuse a past value. Independent executions will never produce the same result. It does not matter if its OpenSSL ↔ OpenSSL, OpenSSL ↔ Java, or Java ↔ Java. They will always produce different results.

jww
  • 97,681
  • 90
  • 411
  • 885