Выработка сеансового ключа через OpenSSL
Друзья! Помогите пожалуйста. Пытаюсь выработать общий ключ по протоколу Диффи-Хеллмана между клиентом и сервером (простеньким, это для учебы). Сколько не пробовал, общий ключ одинаковый не получается. Параметры p и g пересылаю через TCP, точно так же и публичные ключи. Ошибок никаких нет, просто получаются разные значения.
Это клиент
DH *dh = DH_new(); //инициализируем структуру параметров Diffie-Hellman
DH_generate_parameters_ex(dh, 128, 5, NULL); //генерируем p (256 битов) и g (число р потом перезапишем)
DH_generate_key(dh); // генерируем публичный и приватный ключ
unsigned char *recv_buf = (unsigned char*) malloc(1000); //буфер приема данных
memset(recv_buf, 0, 1000);
unsigned char *shared_key; //общий ключ
shared_key = OPENSSL_malloc(sizeof(unsigned char)*(DH_size(dh)));
//////Принимаем число p/////////////////////////////////
unsigned char *p = (unsigned char *) malloc(1000);//
memset(p, 0, 1000); //
while(1) //
{ //
if(recv(sd, recv_buf, 1000, 0)) //
{ //
break; //
} //
sleep(1); //
} //
strncpy(p, recv_buf, strlen(recv_buf)+1); //
BIGNUM* temp_p = BN_new(); //
int a = BN_hex2bn(&temp_p, p); //
dh->p = temp_p; //
memset(recv_buf, 0, 1000); //
////////////////////////////////////////////////////////
//////Принимаем число g/////////////////////////////////
unsigned char *g = (unsigned char *) malloc(1000);//
memset(g, 0, 1000); //
while(1) //
{ //
if(recv(sd, recv_buf, 1000, 0)) //
{ //
break; //
} //
sleep(1); //
} //
strncpy(g, recv_buf, strlen(recv_buf)+1); //
BIGNUM* temp_g = BN_new(); //
a = BN_hex2bn(&temp_g, g); //
dh->g = temp_g; //
memset(recv_buf, 0, 1000); //
////////////////////////////////////////////////////////
//////Принимаем публичный ключ сервера//////////////////////////////
unsigned char *server_pub_key = (unsigned char*) malloc(1000);//
memset(server_pub_key, 0, 1000); //
while(1) //
{ //
if(recv(sd, recv_buf, 1000, 0)) //
{ //
break; //
} //
sleep(1); //
} //
strncpy(server_pub_key, recv_buf, strlen(recv_buf)+1); //
memset(recv_buf, 0, 1000); //
BIGNUM* server_pubkey_bignum = NULL; //
a = BN_hex2bn(&server_pubkey_bignum, server_pub_key); //
////////////////////////////////////////////////////////////////////
//////Отправляем свой публичный ключ////////////////////////////////
unsigned char *pub_key = (unsigned char*) malloc(1000); //
memset(pub_key, 0, 1000); //
pub_key = BN_bn2hex(dh->pub_key); //
while(1) //
{ //
if(send(sd, pub_key, strlen(pub_key)+1, 0)) //
{ //
sleep(1); //
break; //
} //
} //
////////////////////////////////////////////////////////////////////
//////Вычисляем сеансовый ключ/////////////////////////////////////////////////////
if(DH_compute_key(shared_key, server_pubkey_bignum, dh))
{
//Переменные для MD5///////////////////////////////////////////////////////
unsigned char *md5_key;//MD5 - ключ //
md5_key = (unsigned char*) malloc(1000); //
memset(md5_key, 0, 1000); //
size_t len_of_sh_k = strlen(shared_key);//Длина DH-ключа //
unsigned char *md5_res = (unsigned char*) malloc(1000); //
memset(md5_res, 0, 1000); //
///////////////////////////////////////////////////////////////////////////
//Считаем MD5//////////////////////////////////////////////////////////////
md5_res = MD5(shared_key, len_of_sh_k, md5_key);//делаем ключ 128 бит //
///////////////////////////////////////////////////////////////////////////
while(1)
{
memset(recv_buf,0,1000);
if(recv(sd, recv_buf, 1000, 0))
{
break;
}
sleep(1);
}
char* md5_serv = (char*)malloc(1000);
memset(md5_serv,0,1000);
strncpy(md5_serv, recv_buf, strlen(recv_buf)+1);
while(1)
{
memset(recv_buf,0,1000);
if(recv(sd, recv_buf, 1000, 0))
{
break;
}
sleep(1);
}
char* sh_k = (char*)malloc(1000);
memset(sh_k,0,1000);
strncpy(sh_k, recv_buf, strlen(recv_buf)+1);
printf("\nmd5_key1 = %s\nmd5_key2 = %s\nмой sh_k = %s len = %d\nserv sh_k = %s len = %d", md5_res, md5_serv, shared_key, strlen(shared_key), sh_k, strlen(sh_k));
}
close(sd);
getchar();
Это сервер
//сначала сгенерируем параметры диффи-хеллмана
DH *dh = DH_new(); //инициализируем структуру параметров
DH_generate_parameters_ex(dh, 128, 5, NULL); //генерируем p (256 битов) и g
DH_generate_key(dh); // генерируем публичный и приватный ключ
unsigned char *shared_key; //общий ключ
shared_key = OPENSSL_malloc(sizeof(unsigned char)*(DH_size(dh)));
//////Отправляем число p/////////////////////////////////
unsigned char *p = (unsigned char *) malloc(1000); //
memset(p, 0, 1000); //
p = BN_bn2hex(dh->p); //
while(1) //
{ //
if(send(cd, p, strlen(p)+1, 0)) //
{
sleep(1); //
break; //
} //
} //
/////////////////////////////////////////////////////////
//////Отправляем число g/////////////////////////////////
unsigned char *g = (unsigned char *) malloc(1000); //
memset(g, 0, 1000); //
g = BN_bn2hex(dh->g); //
while(1) //
{ //
if(send(cd, g, strlen(g)+1, 0)) //
{
sleep(1); //
break; //
} //
} //
/////////////////////////////////////////////////////////
//////Определяем буфер приема данных//////////////////////////
unsigned char *recv_buf = (unsigned char*) malloc(1000);//
memset(recv_buf, 0, 1000); //
//////////////////////////////////////////////////////////////
//////Отправляем публичный ключ сервера//////////////////////
unsigned char *pub_key = (unsigned char*) malloc(1000);//
memset(pub_key, 0, 1000); //
pub_key = BN_bn2hex(dh->pub_key); //
while(1) //
{ //
if(send(cd, pub_key, strlen(pub_key)+1, 0)) //
{
sleep(1); //
break; //
} //
} //
/////////////////////////////////////////////////////////////
//////Принимаем публичный ключ клиента/////////////////////////////////
unsigned char* client_pubkey_hex = (unsigned char*) malloc(1000);//
memset(client_pubkey_hex, 0, 1000); //
memset(recv_buf, 0, 1000); //
while(1) //
{ //
if(recv(cd, recv_buf, 1000, 0)) //
{ //
break; //
}
sleep(1); //
} //
strncpy(client_pubkey_hex, recv_buf, strlen(recv_buf)+1); //
memset(recv_buf, 0, 1000); //
BIGNUM* client_pubkey_bignum; //
int a = BN_hex2bn(&client_pubkey_bignum, client_pubkey_hex); //
///////////////////////////////////////////////////////////////////////
//////Вычисляем сеансовый ключ/////////////////////////////////////////
if(DH_compute_key(shared_key, client_pubkey_bignum, dh))
{
//Переменные для MD5///////////////////////////////////////////////////////
unsigned char *md5_key;//MD5 - ключ //
md5_key = (unsigned char*) malloc(1000); //
memset(md5_key, 0, 1000); //
size_t len_of_sh_k = strlen(shared_key);//Длина DH-ключа //
unsigned char *md5_res = (unsigned char*) malloc(1000); //
memset(md5_res, 0, 1000); //
///////////////////////////////////////////////////////////////////////////
//Считаем MD5//////////////////////////////////////////////////////////////
md5_res = MD5(shared_key, len_of_sh_k, md5_key);//делаем ключ 128 бит //
///////////////////////////////////////////////////////////////////////////
while(1)
{
if(send(cd, md5_key, strlen(md5_key)+1, 0))
{
sleep(1);
break;
}
}
while(1)
{
if(send(cd, shared_key, strlen(shared_key)+1, 0))
{
sleep(1);
break;
}
}
}
Далее общий ключ и MD5 отправляю просто, чтобы сверить на клиенте. Ломаю голову уже 3 дня, ничего не помогает
Ответы (1 шт):
Разумеется, у вас будут получаться разные ключи! Ведь клиент и сервер используют РАЗНЫЕ группы для генерации ключей. Каждый раз, когда программа вызывает DH_generate_parameters_ex генерируется новый набор параметров.
Поэтому вызов функции DH_generate_parameters_ex в клиенте сгенерирует одно простое число, а в сервере - другое. Для проверки позапускайте вот эту простенькую программу, которая генерирует параметры и печатает их:
#include <openssl/dh.h>
int main() {
DH *dh = DH_new();
DH_generate_parameters_ex(dh, 128, 5, NULL);
DHparams_print_fp(stdout, dh);
return 0;
}
Вы увидите, что каждый раз будет генерироваться новый набор параметров.
Вам не нужно вызывать в клиенте DH_generate_parameters_ex, достаточно DH_new. Соответственно, генерировать ключи в клиенте только после того, как получены P и G от сервера, то есть там, где у вас комментарий //Принимаем публичный ключ сервера.
Эту же задачу можно решить ещё двумя способами.
План А.
Можно пересылать между сервером и клиентом не числа p и g, а сериализованное представление параметров DH сервера. Там сразу будет и p, и g.
Генерация сериализованного представления: функция i2d_DHparams
Создание параметров из сериализованного представления: функция d2i_DHparams
План Б. Но самый простой способ, ИМХО, заранее договориться клиенту и серверу, какую группу они будут использовать. То есть один раз сгенерировать сериализованное представление, а затем его использовать и в клиенте, и в сервере.
Например, вот такая сериализация соответствует DH с простым числом 00:b7:92:27:f2:68:28:e8:4e:ce:2e:f4:21:40:94:81:63 и генератором 5:
#include <openssl/dh.h>
int main() {
// expected prime: 00:b7:92:27:f2:68:28:e8:4e:ce:2e:f4:21:40:94:81:63
unsigned char serialized_params[] = { 0x30, 0x16, 0x02, 0x11, 0x00, 0xb7, 0x92, 0x27, 0xf2, 0x68, 0x28, 0xe8, 0x4e, 0xce, 0x2e, 0xf4, 0x21, 0x40, 0x94, 0x81, 0x63, 0x02, 0x01, 0x05};
unsigned char *buf = serialized_params;
DH *dh_restored = d2i_DHparams(NULL, &buf, sizeof(serialized_params));
DHparams_print_fp(stdout, dh_restored);
return 0;
}
Вот что эта программа печатает:
DH Parameters: (128 bit)
prime:
00:b7:92:27:f2:68:28:e8:4e:ce:2e:f4:21:40:94:
81:63
generator: 5 (0x5)
Как сгенерировать сериализованное представление параметров DH:
#include <openssl/dh.h>
int main() {
DH *dh = DH_new();
DH_generate_parameters_ex(dh, 128, 5, NULL);
DHparams_print_fp(stdout, dh);
unsigned char * param_buf = NULL;
int len;
len = i2d_DHparams(dh, ¶m_buf);
printf("Len %d: [", len);
for (int i = 0; i < len; i++) { printf("0x%02x, ", param_buf[i]); }
printf("]\n");
return 0;
}
Вот пример вывода этой программы:
DH Parameters: (128 bit)
prime:
00:b1:a3:58:c2:2f:66:21:d4:53:c9:71:84:eb:f6:
b5:33
generator: 5 (0x5)
Len 24: [0x30, 0x16, 0x02, 0x11, 0x00, 0xb1, 0xa3, 0x58, 0xc2, 0x2f, 0x66, 0x21, 0xd4, 0x53, 0xc9, 0x71, 0x84, 0xeb, 0xf6, 0xb5, 0x33, 0x02, 0x01, 0x05, ]
Если вы замените в предыдущей программе инциализатор константы serialized_params на {0x30, 0x16, 0x02, 0x11, 0x00, 0xb1, 0xa3, 0x58, 0xc2, 0x2f, 0x66, 0x21, 0xd4, 0x53, 0xc9, 0x71, 0x84, 0xeb, 0xf6, 0xb5, 0x33, 0x02, 0x01, 0x05}, то вы получите DH с простым числом 00:b1:a3:58:c2:2f:66:21:d4:53:c9:71:84:eb:f6:b5:33 и генератором 5.
Ну и тому подобное.