From: Todd Short Date: Mon, 28 Dec 2015 14:13:20 +0000 (-0500) Subject: Add SSL/SSL_CTX_use_cert_and_key() X-Git-Tag: OpenSSL_1_1_1-pre3~138 X-Git-Url: https://git.openssl.org/gitweb/?p=openssl.git;a=commitdiff_plain;h=37933acbeafef6db9a5c5681c1b5174cd91494bc Add SSL/SSL_CTX_use_cert_and_key() Add functions that will do the work of assigning certificate, privatekey and chain certs to an SSL or SSL_CTX. If no privatekey is given, use the publickey. This will permit the keys to pass validation for both ECDSA and RSA. If a private key has already been set for the certificate, it is discarded. A real private key can be set later. This is an all-or-nothing setting of these parameters. Unlike the SSL/SSL_CTX_use_certificate() and SSL/SSL_CTX_use_PrivateKey() functions, the existing cert or privatekey is not modified (i.e. parameters copied). This permits the existing cert/privatekey to be replaced. It replaces the sequence of: * SSL_use_certificate() * SSL_use_privatekey() * SSL_set1_chain() And may actually be faster, as multiple checks are consolidated. The private key can be NULL, if so an ENGINE module needs to contain the actual private key that is to be used. Note that ECDH (using the certificate's ECDSA key) ciphers do not work without the private key being present, based on how the private key is used in ECDH. ECDH does not offer PFS; ECDHE ciphers should be used instead. Reviewed-by: Tim Hudson Reviewed-by: Ben Kaduk (Merged from https://github.com/openssl/openssl/pull/1130) --- diff --git a/crypto/err/openssl.txt b/crypto/err/openssl.txt index 842a4209dc..d867007b2e 100644 --- a/crypto/err/openssl.txt +++ b/crypto/err/openssl.txt @@ -1183,6 +1183,7 @@ SSL_F_SSL_SESSION_SET1_ID:423:SSL_SESSION_set1_id SSL_F_SSL_SESSION_SET1_ID_CONTEXT:312:SSL_SESSION_set1_id_context SSL_F_SSL_SET_ALPN_PROTOS:344:SSL_set_alpn_protos SSL_F_SSL_SET_CERT:191:ssl_set_cert +SSL_F_SSL_SET_CERT_AND_KEY:621:ssl_set_cert_and_key SSL_F_SSL_SET_CIPHER_LIST:271:SSL_set_cipher_list SSL_F_SSL_SET_CT_VALIDATION_CALLBACK:399:SSL_set_ct_validation_callback SSL_F_SSL_SET_FD:192:SSL_set_fd @@ -2525,6 +2526,7 @@ SSL_R_LIBRARY_HAS_NO_CIPHERS:161:library has no ciphers SSL_R_MISSING_DSA_SIGNING_CERT:165:missing dsa signing cert SSL_R_MISSING_ECDSA_SIGNING_CERT:381:missing ecdsa signing cert SSL_R_MISSING_FATAL:256:missing fatal +SSL_R_MISSING_PARAMETERS:290:missing parameters SSL_R_MISSING_RSA_CERTIFICATE:168:missing rsa certificate SSL_R_MISSING_RSA_ENCRYPTING_CERT:169:missing rsa encrypting cert SSL_R_MISSING_RSA_SIGNING_CERT:170:missing rsa signing cert @@ -2535,6 +2537,7 @@ SSL_R_MISSING_SUPPORTED_GROUPS_EXTENSION:209:missing supported groups extension SSL_R_MISSING_TMP_DH_KEY:171:missing tmp dh key SSL_R_MISSING_TMP_ECDH_KEY:311:missing tmp ecdh key SSL_R_NOT_ON_RECORD_BOUNDARY:182:not on record boundary +SSL_R_NOT_REPLACING_CERTIFICATE:289:not replacing certificate SSL_R_NOT_SERVER:284:not server SSL_R_NO_APPLICATION_PROTOCOL:235:no application protocol SSL_R_NO_CERTIFICATES_RETURNED:176:no certificates returned @@ -2577,6 +2580,7 @@ SSL_R_PEM_NAME_BAD_PREFIX:391:pem name bad prefix SSL_R_PEM_NAME_TOO_SHORT:392:pem name too short SSL_R_PIPELINE_FAILURE:406:pipeline failure SSL_R_POST_HANDSHAKE_AUTH_ENCODING_ERR:278:post handshake auth encoding err +SSL_R_PRIVATE_KEY_MISMATCH:288:private key mismatch SSL_R_PROTOCOL_IS_SHUTDOWN:207:protocol is shutdown SSL_R_PSK_IDENTITY_NOT_FOUND:223:psk identity not found SSL_R_PSK_NO_CLIENT_CB:224:psk no client cb diff --git a/doc/man3/SSL_CTX_use_certificate.pod b/doc/man3/SSL_CTX_use_certificate.pod index 22420f97d8..a0c92e6e7b 100644 --- a/doc/man3/SSL_CTX_use_certificate.pod +++ b/doc/man3/SSL_CTX_use_certificate.pod @@ -11,7 +11,8 @@ SSL_CTX_use_PrivateKey_file, SSL_CTX_use_RSAPrivateKey, SSL_CTX_use_RSAPrivateKey_ASN1, SSL_CTX_use_RSAPrivateKey_file, SSL_use_PrivateKey_file, SSL_use_PrivateKey_ASN1, SSL_use_PrivateKey, SSL_use_RSAPrivateKey, SSL_use_RSAPrivateKey_ASN1, -SSL_use_RSAPrivateKey_file, SSL_CTX_check_private_key, SSL_check_private_key +SSL_use_RSAPrivateKey_file, SSL_CTX_check_private_key, SSL_check_private_key, +SSL_CTX_use_cert_and_key, SSL_use_cert_and_key - load certificate and key data =head1 SYNOPSIS @@ -45,6 +46,9 @@ SSL_use_RSAPrivateKey_file, SSL_CTX_check_private_key, SSL_check_private_key int SSL_CTX_check_private_key(const SSL_CTX *ctx); int SSL_check_private_key(const SSL *ssl); + int SSL_CTX_use_cert_and_key(SSL_CTX *ctx, X509 *x, EVP_PKEY *pkey, STACK_OF(X509) *chain, int override); + int SSL_use_cert_and_key(SSL *ssl, X509 *x, EVP_PKEY *pkey, STACK_OF(X509) *chain, int override); + =head1 DESCRIPTION These functions load the certificates and private keys into the SSL_CTX @@ -94,6 +98,19 @@ key pair the new certificate needs to be set with SSL_use_certificate() or SSL_CTX_use_certificate() before setting the private key with SSL_CTX_use_PrivateKey() or SSL_use_PrivateKey(). +SSL_CTX_use_cert_and_key() and SSL_use_cert_and_key() assign the X.509 +certificate B, private key B, and certificate B onto the +corresponding B or B. The B argument must be the private +key of the X.509 certificate B. If the B argument is 0, then +B, B and B are set only if all were not previously set. +If B is non-0, then the certificate, private key and chain certs +are always set. If B is NULL, then the public key of B is used as +the private key. This is intended to be used with hardware (via the ENGINE +inteface) that stores the private key securely, such that it cannot be +accessed by OpenSSL. The reference count of the public key is incremented +(twice if there is no private key); it is not copied nor duplicated. This +allows all private key validations checks to succeed without an actual +private key being assigned via SSL_CTX_use_PrivateKey(), etc. SSL_CTX_use_PrivateKey_ASN1() adds the private key of type B stored at memory location B (length B) to B. @@ -170,7 +187,7 @@ L =head1 COPYRIGHT -Copyright 2000-2016 The OpenSSL Project Authors. All Rights Reserved. +Copyright 2000-2017 The OpenSSL Project Authors. All Rights Reserved. Licensed under the OpenSSL license (the "License"). You may not use this file except in compliance with the License. You can obtain a copy diff --git a/doc/man7/ssl.pod b/doc/man7/ssl.pod index 63b47baa16..03efc52007 100644 --- a/doc/man7/ssl.pod +++ b/doc/man7/ssl.pod @@ -403,6 +403,8 @@ Use the file path to locate trusted CA certificates. =item int B(SSL_CTX *ctx, const char *file, int type); +=item int B(SSL_CTX *ctx, X509 *x, EVP_PKEY *pkey, STACK_OF(X509) *chain, int override); + =item X509 *B(const SSL_CTX *ctx); =item EVP_PKEY *B(const SSL_CTX *ctx); @@ -712,6 +714,8 @@ Returns the current handshake state. =item int B(SSL *ssl, const char *file, int type); +=item int B(SSL *ssl, X509 *x, EVP_PKEY *pkey, STACK_OF(X509) *chain, int override); + =item int B(const SSL *ssl); =item int B(const SSL *ssl); diff --git a/include/openssl/ssl.h b/include/openssl/ssl.h index 825490b5c2..3561dee33b 100644 --- a/include/openssl/ssl.h +++ b/include/openssl/ssl.h @@ -1505,6 +1505,8 @@ __owur int SSL_use_PrivateKey_ASN1(int pk, SSL *ssl, const unsigned char *d, long len); __owur int SSL_use_certificate(SSL *ssl, X509 *x); __owur int SSL_use_certificate_ASN1(SSL *ssl, const unsigned char *d, int len); +__owur int SSL_use_cert_and_key(SSL *ssl, X509 *x509, EVP_PKEY *privatekey, + STACK_OF(X509) *chain, int override); /* serverinfo file format versions */ @@ -1634,6 +1636,8 @@ __owur int SSL_CTX_use_PrivateKey_ASN1(int pk, SSL_CTX *ctx, __owur int SSL_CTX_use_certificate(SSL_CTX *ctx, X509 *x); __owur int SSL_CTX_use_certificate_ASN1(SSL_CTX *ctx, int len, const unsigned char *d); +__owur int SSL_CTX_use_cert_and_key(SSL_CTX *ctx, X509 *x509, EVP_PKEY *privatekey, + STACK_OF(X509) *chain, int override); void SSL_CTX_set_default_passwd_cb(SSL_CTX *ctx, pem_password_cb *cb); void SSL_CTX_set_default_passwd_cb_userdata(SSL_CTX *ctx, void *u); diff --git a/include/openssl/sslerr.h b/include/openssl/sslerr.h index 32fe3662f2..454877df85 100644 --- a/include/openssl/sslerr.h +++ b/include/openssl/sslerr.h @@ -211,6 +211,7 @@ int ERR_load_SSL_strings(void); # define SSL_F_SSL_SESSION_SET1_ID_CONTEXT 312 # define SSL_F_SSL_SET_ALPN_PROTOS 344 # define SSL_F_SSL_SET_CERT 191 +# define SSL_F_SSL_SET_CERT_AND_KEY 621 # define SSL_F_SSL_SET_CIPHER_LIST 271 # define SSL_F_SSL_SET_CT_VALIDATION_CALLBACK 399 # define SSL_F_SSL_SET_FD 192 @@ -566,6 +567,7 @@ int ERR_load_SSL_strings(void); # define SSL_R_MISSING_DSA_SIGNING_CERT 165 # define SSL_R_MISSING_ECDSA_SIGNING_CERT 381 # define SSL_R_MISSING_FATAL 256 +# define SSL_R_MISSING_PARAMETERS 290 # define SSL_R_MISSING_RSA_CERTIFICATE 168 # define SSL_R_MISSING_RSA_ENCRYPTING_CERT 169 # define SSL_R_MISSING_RSA_SIGNING_CERT 170 @@ -576,6 +578,7 @@ int ERR_load_SSL_strings(void); # define SSL_R_MISSING_TMP_DH_KEY 171 # define SSL_R_MISSING_TMP_ECDH_KEY 311 # define SSL_R_NOT_ON_RECORD_BOUNDARY 182 +# define SSL_R_NOT_REPLACING_CERTIFICATE 289 # define SSL_R_NOT_SERVER 284 # define SSL_R_NO_APPLICATION_PROTOCOL 235 # define SSL_R_NO_CERTIFICATES_RETURNED 176 @@ -616,6 +619,7 @@ int ERR_load_SSL_strings(void); # define SSL_R_PEM_NAME_TOO_SHORT 392 # define SSL_R_PIPELINE_FAILURE 406 # define SSL_R_POST_HANDSHAKE_AUTH_ENCODING_ERR 278 +# define SSL_R_PRIVATE_KEY_MISMATCH 288 # define SSL_R_PROTOCOL_IS_SHUTDOWN 207 # define SSL_R_PSK_IDENTITY_NOT_FOUND 223 # define SSL_R_PSK_NO_CLIENT_CB 224 diff --git a/ssl/ssl_err.c b/ssl/ssl_err.c index 34e8ec4076..e367a364bb 100644 --- a/ssl/ssl_err.c +++ b/ssl/ssl_err.c @@ -306,6 +306,8 @@ static const ERR_STRING_DATA SSL_str_functs[] = { {ERR_PACK(ERR_LIB_SSL, SSL_F_SSL_SET_ALPN_PROTOS, 0), "SSL_set_alpn_protos"}, {ERR_PACK(ERR_LIB_SSL, SSL_F_SSL_SET_CERT, 0), "ssl_set_cert"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_SSL_SET_CERT_AND_KEY, 0), + "ssl_set_cert_and_key"}, {ERR_PACK(ERR_LIB_SSL, SSL_F_SSL_SET_CIPHER_LIST, 0), "SSL_set_cipher_list"}, {ERR_PACK(ERR_LIB_SSL, SSL_F_SSL_SET_CT_VALIDATION_CALLBACK, 0), @@ -914,6 +916,7 @@ static const ERR_STRING_DATA SSL_str_reasons[] = { {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_MISSING_ECDSA_SIGNING_CERT), "missing ecdsa signing cert"}, {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_MISSING_FATAL), "missing fatal"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_MISSING_PARAMETERS), "missing parameters"}, {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_MISSING_RSA_CERTIFICATE), "missing rsa certificate"}, {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_MISSING_RSA_ENCRYPTING_CERT), @@ -933,6 +936,8 @@ static const ERR_STRING_DATA SSL_str_reasons[] = { "missing tmp ecdh key"}, {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_NOT_ON_RECORD_BOUNDARY), "not on record boundary"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_NOT_REPLACING_CERTIFICATE), + "not replacing certificate"}, {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_NOT_SERVER), "not server"}, {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_NO_APPLICATION_PROTOCOL), "no application protocol"}, @@ -997,6 +1002,8 @@ static const ERR_STRING_DATA SSL_str_reasons[] = { {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_PIPELINE_FAILURE), "pipeline failure"}, {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_POST_HANDSHAKE_AUTH_ENCODING_ERR), "post handshake auth encoding err"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_PRIVATE_KEY_MISMATCH), + "private key mismatch"}, {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_PROTOCOL_IS_SHUTDOWN), "protocol is shutdown"}, {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_PSK_IDENTITY_NOT_FOUND), diff --git a/ssl/ssl_rsa.c b/ssl/ssl_rsa.c index fe2dd867ad..7eff731e41 100644 --- a/ssl/ssl_rsa.c +++ b/ssl/ssl_rsa.c @@ -1035,3 +1035,114 @@ int SSL_CTX_use_serverinfo_file(SSL_CTX *ctx, const char *file) BIO_free(bin); return ret; } + +static int ssl_set_cert_and_key(SSL *ssl, SSL_CTX *ctx, X509 *x509, EVP_PKEY *privatekey, + STACK_OF(X509) *chain, int override) +{ + int ret = 0; + size_t i; + int j; + int rv; + CERT *c = ssl != NULL ? ssl->cert : ctx->cert; + STACK_OF(X509) *dup_chain = NULL; + EVP_PKEY *pubkey = NULL; + + /* Do all security checks before anything else */ + rv = ssl_security_cert(ssl, ctx, x509, 0, 1); + if (rv != 1) { + SSLerr(SSL_F_SSL_SET_CERT_AND_KEY, rv); + goto out; + } + for (j = 0; j < sk_X509_num(chain); j++) { + rv = ssl_security_cert(ssl, ctx, sk_X509_value(chain, j), 0, 0); + if (rv != 1) { + SSLerr(SSL_F_SSL_SET_CERT_AND_KEY, rv); + goto out; + } + } + + pubkey = X509_get_pubkey(x509); /* bumps reference */ + if (pubkey == NULL) + goto out; + if (privatekey == NULL) { + privatekey = pubkey; + } else { + /* For RSA, which has no parameters, missing returns 0 */ + if (EVP_PKEY_missing_parameters(privatekey)) { + if (EVP_PKEY_missing_parameters(pubkey)) { + /* nobody has parameters? - error */ + SSLerr(SSL_F_SSL_SET_CERT_AND_KEY, SSL_R_MISSING_PARAMETERS); + goto out; + } else { + /* copy to privatekey from pubkey */ + EVP_PKEY_copy_parameters(privatekey, pubkey); + } + } else if (EVP_PKEY_missing_parameters(pubkey)) { + /* copy to pubkey from privatekey */ + EVP_PKEY_copy_parameters(pubkey, privatekey); + } /* else both have parameters */ + + /* Copied from ssl_set_cert/pkey */ +#ifndef OPENSSL_NO_RSA + if ((EVP_PKEY_id(privatekey) == EVP_PKEY_RSA) && + ((RSA_flags(EVP_PKEY_get0_RSA(privatekey)) & RSA_METHOD_FLAG_NO_CHECK))) + /* no-op */ ; + else +#endif + /* check that key <-> cert match */ + if (EVP_PKEY_cmp(pubkey, privatekey) != 1) { + SSLerr(SSL_F_SSL_SET_CERT_AND_KEY, SSL_R_PRIVATE_KEY_MISMATCH); + goto out; + } + } + if (ssl_cert_lookup_by_pkey(pubkey, &i) == NULL) { + SSLerr(SSL_F_SSL_SET_CERT_AND_KEY, SSL_R_UNKNOWN_CERTIFICATE_TYPE); + goto out; + } + + if (!override && (c->pkeys[i].x509 != NULL + || c->pkeys[i].privatekey != NULL + || c->pkeys[i].chain != NULL)) { + /* No override, and something already there */ + SSLerr(SSL_F_SSL_SET_CERT_AND_KEY, SSL_R_NOT_REPLACING_CERTIFICATE); + goto out; + } + + if (chain != NULL) { + dup_chain = X509_chain_up_ref(chain); + if (dup_chain == NULL) { + SSLerr(SSL_F_SSL_SET_CERT_AND_KEY, ERR_R_MALLOC_FAILURE); + goto out; + } + } + + sk_X509_pop_free(c->pkeys[i].chain, X509_free); + c->pkeys[i].chain = dup_chain; + + X509_free(c->pkeys[i].x509); + X509_up_ref(x509); + c->pkeys[i].x509 = x509; + + EVP_PKEY_free(c->pkeys[i].privatekey); + EVP_PKEY_up_ref(privatekey); + c->pkeys[i].privatekey = privatekey; + + c->key = &(c->pkeys[i]); + + ret = 1; + out: + EVP_PKEY_free(pubkey); + return ret; +} + +int SSL_use_cert_and_key(SSL *ssl, X509 *x509, EVP_PKEY *privatekey, + STACK_OF(X509) *chain, int override) +{ + return ssl_set_cert_and_key(ssl, NULL, x509, privatekey, chain, override); +} + +int SSL_CTX_use_cert_and_key(SSL_CTX *ctx, X509 *x509, EVP_PKEY *privatekey, + STACK_OF(X509) *chain, int override) +{ + return ssl_set_cert_and_key(NULL, ctx, x509, privatekey, chain, override); +} diff --git a/util/libssl.num b/util/libssl.num index 48c5eca19d..b299035042 100644 --- a/util/libssl.num +++ b/util/libssl.num @@ -477,3 +477,5 @@ SSL_stateless 477 1_1_1 EXIST::FUNCTION: SSL_verify_client_post_handshake 478 1_1_1 EXIST::FUNCTION: SSL_force_post_handshake_auth 479 1_1_1 EXIST::FUNCTION: SSL_export_keying_material_early 480 1_1_1 EXIST::FUNCTION: +SSL_CTX_use_cert_and_key 481 1_1_1 EXIST::FUNCTION: +SSL_use_cert_and_key 482 1_1_1 EXIST::FUNCTION: