Как сделать ECDHE рукопожатие без экспортируемого закрытого ключа
Я создаю движок OpenSSL, который реализует ECDSA_METHOD, который включает функции создания подписи и проверки подписи. Поскольку единственное использование закрытого ключа ECDHE связано с созданием подписи, экспорт ключа из механизма и его представление в другом месте не требуется.
Однако, если я не предоставляю закрытый ключ для SSL_Context через функцию SSL_set_private_key, SSL рукопожатие завершается с ошибкой ниже:
error:14094410:SSL routines:ssl3_read_bytes:sslv3 alert handshake failure
Я также пытался предоставить фиктивный ключ (тот, который не связан с открытым ключом в сертификате) для функции SSL_set_private_key, но эта функция проверяет, совпадают ли закрытые / открытые ключи, и выдает ошибку о плохом сертификате, если они не т.
Похоже, что openssl позволяет обойти эту проверку в некоторых случаях, например, это то, что я нашел в ssl / ssl_rsa.c
#ifndef OPENSSL_NO_RSA
/*
* Don't check the public/private key, this is mostly for smart
* cards.
*/
if ((pkey->type == EVP_PKEY_RSA) &&
(RSA_flags(pkey->pkey.rsa) & RSA_METHOD_FLAG_NO_CHECK)) ;
else
#endif
if (!X509_check_private_key(c->pkeys[i].x509, pkey)) {
X509_free(c->pkeys[i].x509);
c->pkeys[i].x509 = NULL;
return 0;
}
Я думаю, мне нужно что-то подобное для ключа EC, но я нигде не нашел его. Любые другие решения также приветствуются.
2 ответа
Любые другие решения также приветствуются.
Это может быть не единственный вариант, который у вас есть, но я думаю, что вы можете достичь того, что вы ищете, создав свой собственный EVP_PKEY_METHOD
и выполнение своих функций по мере необходимости. Таким образом, вы можете сохранить дескриптор на свой собственный, например, ключ на основе смарт-карты, а затем в нужный момент вызывать надлежащие методы подписи. Вы должны установить правильные методы с EVP_PKEY_meth_set_Xyz()
функции, как EVP_PKEY_meth_set_sign(<yourSigningFunction>)
, Например, если вы используете Windows Crypto API, вам придется вызывать NCryptSignHash()
от вашей функции подписи. Таким образом, вам не нужно экспортировать закрытый ключ из хранилища ключей Windows, чтобы получить подпись.
Я делал это раньше, и единственное, с чем я столкнулся (кроме отсутствия документации и примеров), это отсутствие функциональности хранилища ключей на EVP
уровень. Похоже, что здесь идет работа, как вы можете видеть здесь. В качестве обходного пути мне пришлось выбирать ключи / сертификаты из хранилища как часть механизма генерации ключей, и он на самом деле не предназначен для этого.
Если вы решите пойти по этому пути, то будьте готовы к нескольким неделям проб и ошибок.
Вот как вы можете обойти правила проверки openssl, предоставив EC_KEY открытый ключ, равный общедоступному сертификату, а закрытый ключ - любое ненулевое значение (в моем примере я просто установил его равным X координата открытого ключа). После того, как ключ создан и сохранен в файле, его можно передать как обычный закрытый ключ в SSL_Context.
Я думаю, идеалистически openssl должен решать эту проблему более систематическим и прозрачным способом, но пока это не сделано, предлагаемое решение можно использовать в качестве обходного пути:
#include <string.h>
#include <stdio.h>
#include <openssl/ssl.h>
#include <openssl/x509v3.h>
static char * my_prog = "dummykey";
static char * key_file = NULL;
static char * cert_file = NULL;
int verbose = 0;
static void print_help() {
fprintf(stderr,"Version: %s\nUSAGE: %s -cert in_cert_file -key out_key_file\n",
VERSION, my_prog);
}
static void parse_args(int argc, char** argv) {
argc--;
argv++;
while (argc >= 1) {
if (!strcmp(*argv,"-key")) {
key_file = *++argv;
argc--;
}
else if (!strcmp(*argv,"-cert")) {
cert_file = *++argv;
argc--;
}
else if (!strcmp(*argv,"-v")) {
verbose = 1;
}
else {
fprintf(stderr, "%s: Invalid param: %s\n", my_prog, *argv);
print_help();
exit(1);
}
argc--;
argv++;
}
if (key_file == NULL || cert_file == NULL ) {
print_help();
exit(1);
}
}
int get_curve_nid(X509 *c) {
int ret = 0;
if (c->cert_info->key->algor->parameter) {
ASN1_TYPE *p = c->cert_info->key->algor->parameter;
if (p && p->type == V_ASN1_OBJECT) {
ret = OBJ_obj2nid(c->cert_info->key->algor->parameter->value.object);
}
}
return ret;
}
int main(int argc, char** argv) {
X509 *c=NULL;
FILE *fp=NULL;
FILE *ofp=NULL;
EC_POINT *ec_point = NULL;
BIGNUM *x = NULL;
BIGNUM *y = NULL;
EC_KEY *ec_key = NULL;
EC_GROUP *grp = NULL;
parse_args(argc, argv);
fp = fopen(cert_file, "r");
if (!fp) {
fprintf(stderr,"%s: Can't open %s\n", my_prog, cert_file);
return 1;
}
c = PEM_read_X509 (fp, NULL, (int (*) ()) 0, (void *) 0);
if (c) {
x = BN_new();
y = BN_new();
int len = c->cert_info->key->public_key->length-1;
BN_bin2bn(c->cert_info->key->public_key->data+1, len/2, x);
BN_bin2bn(c->cert_info->key->public_key->data+1+len/2, len/2, y);
EC_GROUP *grp = EC_GROUP_new_by_curve_name(get_curve_nid(c));
ec_key = EC_KEY_new();
int sgrp = EC_KEY_set_group(ec_key, grp);
int sprk = EC_KEY_set_private_key(ec_key, x);
if (sgrp && sprk) {
ec_point = EC_POINT_new(grp);
int ac = EC_POINT_set_affine_coordinates_GFp(grp, ec_point, x, y, BN_CTX_new());
int spub =EC_KEY_set_public_key(ec_key, ec_point);
ofp = fopen(key_file, "w");
int r = 0;
if (ofp) {
r = PEM_write_ECPrivateKey(ofp, ec_key, NULL, NULL, 0, NULL, NULL);
if (!r)
fprintf(stderr,"%s: Can't write EC key %p to %s\n", my_prog, ec_key, key_file);
}
else {
fprintf(stderr,"%s: Can't open %s\n", my_prog, key_file);
}
}
}
if (ec_key)
EC_KEY_free(ec_key);
if (grp)
EC_GROUP_free(grp);
if (x)
BN_free(x);
if (y)
BN_free(y);
if (c)
X509_free (c);
if (fp)
fclose(fp);
if (ofp)
fclose(ofp);
return 0;
}