23

我正在使用 OpenSSL 的 c 库来生成椭圆曲线 Diffie-Hellman (ECDH) 密钥对,遵循此处的第一个代码示例。它用这一行掩盖了公钥的实际交换:

peerkey = get_peerkey(pkey);

变量和pkey返回值都是 type EVP *pkey包含前面生成的公钥、私钥和参数,返回值只包含对端的公钥。所以这提出了三个问题:

  1. 实际上如何get_peerkey()仅提取公钥pkey以发送给对等方?
  2. 代码如何从中提取私钥和参数pKey以存储它们以供密钥交换后使用?
  3. 如何从对等方的原始公钥get_peerkey()生成新结构?EVP_PKEY

我见过 OpenSSL 函数EVP_PKEY_print_public(), EVP_PKEY_print_private(),EVP_PKEY_print_params()但这些是用于生成人类可读的输出。而且我还没有找到任何将人类可读的公钥转换回EVP_PKEY结构的等价物。

4

2 回答 2

48

要回答我自己的问题,私钥和公钥有不同的路径。

序列化公钥:

  1. 将 EVP_PKEY 传递给 EVP_PKEY_get1_EC_KEY() 以获取 EC_KEY。
  2. 将 EC_KEY 传递给 EC_KEY_get0_public_key() 以获取 EC_POINT。
  3. 将 EC_POINT 传递给 EC_POINT_point2oct() 以获取八位字节,它们只是 unsigned char *。

要反序列化公钥:

  1. 将八位字节传递给 EC_POINT_oct2point() 以获取 EC_POINT。
  2. 将 EC_POINT 传递给 EC_KEY_set_public_key() 以获取 EC_KEY。
  3. 将 EC_KEY 传递给 EVP_PKEY_set1_EC_KEY 以获取 EVP_KEY。

序列化私钥:

  1. 将 EVP_PKEY 传递给 EVP_PKEY_get1_EC_KEY() 以获取 EC_KEY。
  2. 将 EC_KEY 传递给 EC_KEY_get0_private_key() 以获得 BIGNUM。
  3. 将 BIGNUM 传递给 BN_bn2mpi() 以获取 mpi,这是一种写入 unsigned char * 的格式。

反序列化私钥:

  1. 将 mpi 传递给 BN_mpi2bn() 以获得 BIGNUM。
  2. 将 BIGNUM 传递给 EC_KEY_set_private_key() 以获取 EC_KEY。
  3. 将 EC_KEY 传递给 EVP_PKEY_set1_EC_KEY 以获取 EVP_KEY。

也可以将 BIGNUM 转换为十六进制、十进制或“bin”,尽管我认为 mpi 使用的字节最少。

于 2013-09-04T00:42:13.217 回答
1

上面的实现似乎太复杂了。openssl/evp.h具有函数i2d_PublicKey()d2i_PublicKey()分别转换为公钥的二进制表示和从公钥的二进制表示转换(并且私钥有等效的函数 - 参见:https ://www.openssl.org/docs/manmaster/man3/d2i_PublicKey.html )

一个小代码示例:

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

我正在使用 C++ 向量来包含二进制数据,但当然你也可以使用 C 风格的数组 :-)

我绝对不是 OpenSSL 专家,所以如果我在这个实现中做错了什么,请告诉我:-p

于 2021-08-17T14:13:59.197 回答