如何访问 OpenSSL 的 EVP_PKEY 结构中的原始 ECDH 公钥、私钥和参数?
Posted
技术标签:
【中文标题】如何访问 OpenSSL 的 EVP_PKEY 结构中的原始 ECDH 公钥、私钥和参数?【英文标题】:How does one access the raw ECDH public key, private key and params inside OpenSSL's EVP_PKEY structure? 【发布时间】:2013-08-11 21:49:19 【问题描述】:我正在使用 OpenSSL 的 c 库生成椭圆曲线 Diffie-Hellman (ECDH) 密钥对,遵循第一个代码示例 here。它用这一行掩盖了公钥的实际交换:
peerkey = get_peerkey(pkey);
pkey
变量和返回值都是EVP *
类型。 pkey
包含前面生成的公钥、私钥和参数,返回值只包含对端的公钥。所以这提出了三个问题:
get_peerkey()
如何实际上仅从 pkey
提取公钥以发送给对等方?
代码如何从pKey
中提取私钥和参数以存储它们以供密钥交换后使用?
get_peerkey()
如何从对等方的原始公钥生成新的EVP_PKEY
结构?
我见过 OpenSSL 函数 EVP_PKEY_print_public()
、EVP_PKEY_print_private()
和 EVP_PKEY_print_params()
,但这些函数用于生成人类可读的输出。而且我还没有找到任何将人类可读的公钥转换回EVP_PKEY
结构的等价物。
【问题讨论】:
【参考方案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
【讨论】:
问题标记为 C,但这是 C++ 答案。【参考方案2】:为了回答我自己的问题,私钥和公钥有不同的路径。
序列化公钥:
-
将 EVP_PKEY 传递给 EVP_PKEY_get1_EC_KEY() 以获取 EC_KEY。
将 EC_KEY 传递给 EC_KEY_get0_public_key() 以获取 EC_POINT。
将 EC_POINT 传递给 EC_POINT_point2oct() 以获取八位字节,它们只是无符号字符 *。
要反序列化公钥:
-
将八位字节传递给 EC_POINT_oct2point() 以获取 EC_POINT。
将 EC_POINT 传递给 EC_KEY_set_public_key() 以获取 EC_KEY。
将 EC_KEY 传递给 EVP_PKEY_set1_EC_KEY 以获取 EVP_KEY。
序列化私钥:
-
将 EVP_PKEY 传递给 EVP_PKEY_get1_EC_KEY() 以获取 EC_KEY。
将 EC_KEY 传递给 EC_KEY_get0_private_key() 以获得 BIGNUM。
将 BIGNUM 传递给 BN_bn2mpi() 以获取 mpi,这是写入的格式
无符号字符 *.
反序列化私钥:
-
将 mpi 传递给 BN_mpi2bn() 以获得 BIGNUM。
将 BIGNUM 传递给 EC_KEY_set_private_key() 以获取 EC_KEY。
将 EC_KEY 传递给 EVP_PKEY_set1_EC_KEY 以获取 EVP_KEY。
也可以将 BIGNUM 转换为十六进制、十进制或“bin”,尽管我认为 mpi 使用的字节最少。
【讨论】:
感谢您提供后续答复! 反序列化公钥可能已经工作了 4 年零 5 个月,但今天听起来很复杂!!! :p EVP_PKEY_get1_EC_KEY 应该从八位字节创建 EC_POINT 但它从输入中获取 EC_POINT *!我试图通过 EC_POINT_new 来创建它,但后来无法使用 create EVP_KEY 创建密钥!以上是关于如何访问 OpenSSL 的 EVP_PKEY 结构中的原始 ECDH 公钥、私钥和参数?的主要内容,如果未能解决你的问题,请参考以下文章