How does one access the raw ECDH public key, private key and params inside OpenSSL's EVP_PKEY structure?

Bob Whiteman picture Bob Whiteman · Aug 9, 2013 · Viewed 9k times · Source

I'm using OpenSSL's c library to generate an elliptic curve Diffie-Hellman (ECDH) key pair, following the first code sample here. It glosses over the actual exchange of public keys with this line:

peerkey = get_peerkey(pkey);

The pkey variable and the return value are both of type EVP *. pkey contains the public key, private key, and params generated earlier, and the return value only contains the peer's public key. So this raises three questions:

  1. How would get_peerkey() actually extract just the public key from pkey for sending to the peer?
  2. How would the code extract the private key and params from pKey to store them for later use after the key exchange?
  3. How would get_peerkey() generate a new EVP_PKEY structure from the peer's raw public key?

I've seen the OpenSSL functions EVP_PKEY_print_public(), EVP_PKEY_print_private(), and EVP_PKEY_print_params() but these are for generating human-readable output. And I haven't found any equivalent for converting a human-readable public key back into an EVP_PKEY structure.

Answer

Bob Whiteman picture Bob Whiteman · Sep 4, 2013

To answer my own question, there's a different path for the private key and the public key.

To serialize the public key:

  1. Pass the EVP_PKEY to EVP_PKEY_get1_EC_KEY() to get an EC_KEY.
  2. Pass the EC_KEY to EC_KEY_get0_public_key() to get an EC_POINT.
  3. Pass the EC_POINT to EC_POINT_point2oct() to get octets, which are just unsigned char *.

To deserialize the public key:

  1. Pass the octets to EC_POINT_oct2point() to get an EC_POINT.
  2. Pass the EC_POINT to EC_KEY_set_public_key() to get an EC_KEY.
  3. Pass the EC_KEY to EVP_PKEY_set1_EC_KEY to get an EVP_KEY.

To serialize the private key:

  1. Pass the EVP_PKEY to EVP_PKEY_get1_EC_KEY() to get an EC_KEY.
  2. Pass the EC_KEY to EC_KEY_get0_private_key() to get a BIGNUM.
  3. Pass the BIGNUM to BN_bn2mpi() to get an mpi, which is a format written to unsigned char *.

To deserialize the private key:

  1. Pass the mpi to BN_mpi2bn() to get a BIGNUM.
  2. Pass the BIGNUM to EC_KEY_set_private_key() to get an EC_KEY.
  3. Pass the EC_KEY to EVP_PKEY_set1_EC_KEY to get an EVP_KEY.

It is also possible to convert the BIGNUM to hex, decimal, or "bin", although I think that mpi used the fewest bytes.