我正在尝试使自己熟悉OpenSSL Diffie Hellman的功能,为此,我试图创建一个简单的程序,该程序将生成两组Diffie Hellman私钥和公钥,然后得出共享密钥。我已经遵循了OpenSSL Wiki上的Diffie Hellman教程,并且能够生成密钥,但是无法导出共享密钥。我的C ++(Linux)代码如下:
#include <iostream>
#include <openssl/dh.h>
#include <openssl/engine.h>
#include <openssl/err.h>
#include <openssl/evp.h>
#include <openssl/rsa.h>
int main(int argc, char** argv)
{
EVP_PKEY* params;
EVP_PKEY_CTX *kctx1, *kctx2, *dctx1, *dctx2;
unsigned char *skey1, *skey2;
size_t skeylen1, skeylen2;
EVP_PKEY *dh_1, *dh_2;
BIO* bio_out = NULL;
int result = 0;
ENGINE* eng;
BIO* fp = BIO_new_fp(stdout, BIO_NOCLOSE);
// Initalise Diffie Hellman PKEY for client 1
if(NULL == (dh_1 = EVP_PKEY_new())) {
std::cout << "error 1" << std::endl;
}
// Initalise Diffie Hellman PKEY for client 2
if(NULL == (dh_2 = EVP_PKEY_new())) {
std::cout << "error 2" << std::endl;
}
// Initalise Diffie Hellman parameter PKEY
if(NULL == (params = EVP_PKEY_new())) {
std::cout << "error 3" << std::endl;
}
// Set Diffie Hellman paramerers
if(1 != EVP_PKEY_set1_DH(params, DH_get_2048_256())) {
std::cout << "error 4" << std::endl;
}
// Initalise client 1 PKEY Context
if(!(kctx1 = EVP_PKEY_CTX_new(params, NULL))) {
std::cout << "error 5" << std::endl;
}
// Initalise client 2 PKEY Context
if(!(kctx2 = EVP_PKEY_CTX_new(params, NULL))) {
std::cout << "error 6" << std::endl;
}
if(!kctx1) {
std::cout << "error 7" << std::endl;
}
if(!kctx2) {
std::cout << "error 8" << std::endl;
}
// Initalise both contexts key generators
if(1 != EVP_PKEY_keygen_init(kctx1)) {
std::cout << "error 9" << std::endl;
}
if(1 != EVP_PKEY_keygen_init(kctx2)) {
std::cout << "error 10" << std::endl;
}
// Generate DH public and private keys for client 1
if(1 != EVP_PKEY_keygen(kctx1, &dh_1)) {
std::cout << "error 11" << std::endl;
}
// Generate DH public and private keys for client 2
if(1 != EVP_PKEY_keygen(kctx2, &dh_2)) {
std::cout << "error 12" << std::endl;
}
// EVP_PKEY_print_public(fp, dh_1, 3, NULL);
// EVP_PKEY_print_public(fp, dh_2, 3, NULL);
// Create key derivation context
if(NULL == (dctx1 = EVP_PKEY_CTX_new(dh_1, NULL))) {
std::cout << "error 13" << std::endl;
}
if(!dctx1) {
std::cout << "error 14" << std::endl;
}
// Initalise first key derivation context
if(1 != EVP_PKEY_derive_init(dctx1)) {
std::cout << "error 15" << std::endl;
}
if(1 != EVP_PKEY_check(dctx1)) {
std::cout << "error 16" << std::endl;
}
if(1 != EVP_PKEY_param_check(dctx1)) {
std::cout << "error 17" << std::endl;
}
// Set first key derivation context peer key to the second DH PKEY
if(1 != EVP_PKEY_derive_set_peer(dctx1, dh_2)) {
std::cout << "error 18" << std::endl;
}
if(1 != EVP_PKEY_public_check(dctx1)) {
std::cout << "error 19" << std::endl;
}
/* Determine buffer length */
if(EVP_PKEY_derive(dctx1, NULL, &skeylen1) <= 0) {
}
// Assign memory for shared key variable
skey1 = (unsigned char*)OPENSSL_malloc(skeylen1);
if(result = EVP_PKEY_derive(dctx1, skey1, &skeylen1) <= 0) {
std::cout << "Key: " << skey1 << std::endl;
for(int i = 0; i < skeylen1; i++) {
std::cout << std::hex << (unsigned int)skey1[i];
}
}
ERR_print_errors_fp(stdout);
std::cout << result << std::endl;
}
我主要关心的是:
谢谢!
我收到以下错误,并且没有共享密钥:
140690271102784:error:05079079:Diffie-Hellman routines:DH_check_ex:unable to check generator:../crypto/dh/dh_check.c:92:
140690271102784:error:05079076:Diffie-Hellman routines:DH_check_ex:check p not safe prime:../crypto/dh/dh_check.c:96:
140690271102784:error:0507B07B:Diffie-Hellman routines:DH_check_pub_key_ex:check pubkey too large:../crypto/dh/dh_check.c:190:
140690271102784:error:0507B07A:Diffie-Hellman routines:DH_check_pub_key_ex:check pubkey invalid:../crypto/dh/dh_check.c:192:
当我使用时
if(NULL == (dctx1 = EVP_PKEY_CTX_new(dh_1, NULL))) {
std::cout << "error 13" << std::endl;
}
并且出现以下错误
error 16
error 19
Key: �ȭ��U
c0c8add7c4550000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
139939742943040:error:060BA096:digital envelope routines:EVP_PKEY_check:operation not supported for this keytype:../crypto/evp/pmeth_gn.c:188:
139939742943040:error:0507C07D:Diffie-Hellman routines:dh_pkey_public_check:missing pubkey:../crypto/dh/dh_ameth.c:517:
139939742943040:error:05066064:Diffie-Hellman routines:compute_key:no private value:../crypto/dh/dh_key.c:183:
1
当我将“ EVP_PKEY_CTX_new(dh_1,NULL)”更改为“ EVP_PKEY_CTX_new(params,NULL)”时,像这样:
if(NULL == (dctx1 = EVP_PKEY_CTX_new(params, NULL))) {
std::cout << "error 13" << std::endl;
}
这两个变量之间的区别在于,“ dh_1”存储DH密钥对,而“ params”包含DH参数。似乎派生上下文在某种程度上启发了后者,尽管它没有与之相关的私钥和公钥。
使用您发布的代码版本,我得到的错误略有不同。我从您的代码中获得了以下输出(与OpenSSL 1.1.1链接):
error 16
error 17
140673674348352:error:060BA096:digital envelope routines:EVP_PKEY_check:operation not supported for this keytype:crypto/evp/pmeth_gn.c:187:
140673674348352:error:05079076:Diffie-Hellman routines:DH_check_ex:check p not safe prime:crypto/dh/dh_check.c:93:
0
第一个“错误16”输出仅是因为OpenSSL当前不支持Diffie-Hellman密钥的EVP_PKEY_check()
调用。因此可以放心地忽略它。
第二个“错误17”输出(与错误队列中的“ check p not safe prime”错误相关联)更成问题。这是因为Diffie-Hellman键有两种不同的类型。默认情况下,OpenSSL使用PKCS#3 Diffie-Hellman密钥。但是,它也支持X9.42 Diffie-Hellman键。函数DH_get_2048_256()
为X9.42键提供了一组内置参数。但是,函数EVP_PKEY_set1_DH()
期望提供的DH对象是PKCS#3密钥。
这实际上对我来说似乎是OpenSSL中的一个错误(EVP_PKEY_set1_DH()
应该真正检测出它是哪种类型的密钥并做正确的事情),因此我为此提出了以下OpenSSL问题:https://github.com/openssl/openssl/issues/10592
您可以通过将EVP_PKEY_set1_DH()
调用替换为EVP_PKEY_assign()
并将其指定为EVP_PKEY_DHX
的类型来解决此问题,如下所示:
if(1 != EVP_PKEY_assign(params, EVP_PKEY_DHX, DH_get_2048_256())) {
std::cout << "error 4" << std::endl;
}
或者,您可能只使用PKCS#3参数。
最后,没有得到任何共享机密的原因是,仅当EVP_PKEY_derive()
调用失败时才打印它!它实际上是成功的!
我对您的代码进行了以下更改,它对我有用:
--- derive.cpp 2019-12-09 11:11:15.493349734 +0000
+++ derive-new.cpp 2019-12-09 11:14:59.348715074 +0000
@@ -37,7 +37,7 @@
}
// Set Diffie Hellman paramerers
- if(1 != EVP_PKEY_set1_DH(params, DH_get_2048_256())) {
+ if(1 != EVP_PKEY_assign(params, EVP_PKEY_DHX, DH_get_2048_256())) {
std::cout << "error 4" << std::endl;
}
@@ -96,9 +96,11 @@
std::cout << "error 15" << std::endl;
}
+#if 0
if(1 != EVP_PKEY_check(dctx1)) {
std::cout << "error 16" << std::endl;
}
+#endif
if(1 != EVP_PKEY_param_check(dctx1)) {
std::cout << "error 17" << std::endl;
@@ -120,7 +122,7 @@
// Assign memory for shared key variable
skey1 = (unsigned char*)OPENSSL_malloc(skeylen1);
- if(result = EVP_PKEY_derive(dctx1, skey1, &skeylen1) <= 0) {
+ if((result = EVP_PKEY_derive(dctx1, skey1, &skeylen1)) > 0) {
std::cout << "Key: " << skey1 << std::endl;
for(int i = 0; i < skeylen1; i++) {