Add SSL/SSL_CTX_use_cert_and_key()
authorTodd Short <tshort@akamai.com>
Mon, 28 Dec 2015 14:13:20 +0000 (09:13 -0500)
committerBen Kaduk <kaduk@mit.edu>
Fri, 9 Mar 2018 16:28:04 +0000 (10:28 -0600)
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 <tjh@openssl.org>
Reviewed-by: Ben Kaduk <kaduk@mit.edu>
(Merged from https://github.com/openssl/openssl/pull/1130)

crypto/err/openssl.txt
doc/man3/SSL_CTX_use_certificate.pod
doc/man7/ssl.pod
include/openssl/ssl.h
include/openssl/sslerr.h
ssl/ssl_err.c
ssl/ssl_rsa.c
util/libssl.num

index 842a420..d867007 100644 (file)
@@ -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
index 22420f9..a0c92e6 100644 (file)
@@ -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<x>, private key B<key>, and certificate B<chain> onto the
+corresponding B<ssl> or B<ctx>. The B<pkey> argument must be the private
+key of the X.509 certificate B<x>. If the B<override> argument is 0, then
+B<x>, B<pkey> and B<chain> are set only if all were not previously set.
+If B<override> is non-0, then the certificate, private key and chain certs
+are always set. If B<pkey> is NULL, then the public key of B<x> 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<pk>
 stored at memory location B<d> (length B<len>) to B<ctx>.
@@ -170,7 +187,7 @@ L<SSL_CTX_add_extra_chain_cert(3)>
 
 =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
index 63b47ba..03efc52 100644 (file)
@@ -403,6 +403,8 @@ Use the file path to locate trusted CA certificates.
 
 =item int B<SSL_CTX_use_certificate_file>(SSL_CTX *ctx, const char *file, int type);
 
+=item int B<SSL_CTX_use_cert_and_key>(SSL_CTX *ctx, X509 *x, EVP_PKEY *pkey, STACK_OF(X509) *chain, int override);
+
 =item X509 *B<SSL_CTX_get0_certificate>(const SSL_CTX *ctx);
 
 =item EVP_PKEY *B<SSL_CTX_get0_privatekey>(const SSL_CTX *ctx);
@@ -712,6 +714,8 @@ Returns the current handshake state.
 
 =item int B<SSL_use_certificate_file>(SSL *ssl, const char *file, int type);
 
+=item int B<SSL_use_cert_and_key>(SSL *ssl, X509 *x, EVP_PKEY *pkey, STACK_OF(X509) *chain, int override);
+
 =item int B<SSL_version>(const SSL *ssl);
 
 =item int B<SSL_want>(const SSL *ssl);
index 825490b..3561dee 100644 (file)
@@ -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);
index 32fe366..454877d 100644 (file)
@@ -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
index 34e8ec4..e367a36 100644 (file)
@@ -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),
index fe2dd86..7eff731 100644 (file)
@@ -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);
+}
index 48c5eca..b299035 100644 (file)
@@ -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: