Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Tags:

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.

like image 586
Bob Whiteman Avatar asked Aug 09 '13 20:08

Bob Whiteman


2 Answers

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.

like image 119
Bob Whiteman Avatar answered Sep 28 '22 04:09

Bob Whiteman


The implementation above seems too complicated. openssl/evp.h has functions i2d_PublicKey() and d2i_PublicKey() to respectively convert to and from a binary representation of the public key (and there are equivalent functions for the private key - see: https://www.openssl.org/docs/manmaster/man3/d2i_PublicKey.html)

A small code example:

vector<unsigned char> ecdhPubkeyData(EVP_PKEY *key) {     int len = i2d_PublicKey(key, 0); // with 0 as second arg it gives length     vector<unsigned char> ret(len);     unsigned char *ptr = ret.data();     len = i2d_PublicKey(key, &ptr);     return ret; }  // Make sure you free the returned pointer when you are done with it EVP_PKEY *ecdhPubkeyFromData(vector <unsigned char> const &pubkeyData) {       // You do need to put in in an existing EVP_PKEY that is assigned         // an EC_KEY, because it needs to know what curve you use     EC_KEY *ec_key = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1);     EVP_PKEY *ret = EVP_PKEY_new();     EVP_PKEY_assign_EC_KEY(ret, ec_key);     unsigned char const *ptr = pubkeyData.data();     d2i_PublicKey(EVP_PKEY_EC, &ret, &ptr, pubkeyData.size());     return ret; } // PS: In a real example you want to check if any of these functions // return NULL or some error code 

I am using C++ vectors to contain the binary data, but of course you could just use C-style arrays too :-)

I am absolutely not an OpenSSL expert, so let me know if I am doing something horribly wrong in this implementation :-p

like image 37
Vincent Tonkes Avatar answered Sep 28 '22 04:09

Vincent Tonkes