From: Joe Orton Date: Tue, 8 May 2018 12:39:22 +0000 (+0000) Subject: mod_ssl: Add support for loading TLS certificates through the PKCS#11 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=bba7b9805a95ee505178e13eac1f7c82697f741d;p=apache mod_ssl: Add support for loading TLS certificates through the PKCS#11 engine. * modules/ssl/ssl_util.c (modssl_is_engine_id): Renamed from modssl_is_engine_key. * modules/ssl/ssl_engine_config.c (ssl_cmd_SSLCertificateKeyFile): Adjust accordingly. (ssl_cmd_SSLCertificateFile): Also allow ENGINE cert ids. * modules/ssl/ssl_engine_pphrase.c (modssl_load_engine_keypair): Rename from modssl_load_engine_key; load certificate if cert id is passed. * modules/ssl/ssl_engine_init.c (ssl_init_server_certs): Optionally load the certificate from the engine as well. * docs/manual/: Update manual. git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@1831168 13f79535-47bb-0310-9956-ffa450edef68 --- diff --git a/CHANGES b/CHANGES index 92a0625ca9..2c7e1f10a7 100644 --- a/CHANGES +++ b/CHANGES @@ -3,8 +3,9 @@ Changes with Apache 2.5.1 *) mod_ldap: Abort on LDAP locking errors. [Eric Covener] - *) mod_ssl: Support loading private keys from an OpenSSL engine using a - PKCS#11 URI. [Anderson Sasaki , Joe Orton] + *) mod_ssl: Support loading certificates and private keys from the + PKCS#11 OpenSSL engine. [Anderson Sasaki , + Joe Orton] *) mod_slomem_shm: Handle a generation number when the slotmem size changes, modifying the number of proxy balancers or balancer members on restart diff --git a/docs/log-message-tags/next-number b/docs/log-message-tags/next-number index 4e271c9d62..2e38526ea1 100644 --- a/docs/log-message-tags/next-number +++ b/docs/log-message-tags/next-number @@ -1 +1 @@ -10136 +10137 diff --git a/docs/manual/mod/mod_ssl.xml b/docs/manual/mod/mod_ssl.xml index 4160989b97..bd3f9cd41c 100644 --- a/docs/manual/mod/mod_ssl.xml +++ b/docs/manual/mod/mod_ssl.xml @@ -866,15 +866,16 @@ SSLCipherSuite RSA:!EXP:!NULL:+HIGH:+MEDIUM:-LOW SSLCertificateFile -Server PEM-encoded X.509 certificate data file -SSLCertificateFile file-path +Server PEM-encoded X.509 certificate data file or token identifier +SSLCertificateFile file-path|certid server config virtual host +certid available in 2.5.1 and later.

-This directive points to a file with certificate data in PEM format. -At a minimum, the file must include an end-entity (leaf) certificate. +This directive points to a file with certificate data in PEM format, or the certificate identifier through a configured cryptographic token. +If using a PEM file, at minimum, the file must include an end-entity (leaf) certificate. The directive can be used multiple times (referencing different filenames) to support multiple algorithms for server authentication - typically RSA, DSA, and ECC. The number of supported algorithms depends on the @@ -915,6 +916,18 @@ after the certificates using a separate key file. If the private key is encrypted, the pass phrase dialog is forced at startup time.

+

As an alternative to storing certificates and private keys in +files, a certificate identifier can be used to identify a certificate +stored in a token. Currently, only PKCS#11 URIs are +recognized as certificate identifiers, and can be used in conjunction +with the OpenSSL pkcs11 engine configured with SSLCryptoDevice. If SSLCertificateKeyFile is omitted, the +certificate and private key can be loaded through the single +identifier specified with SSLCertificateFile.

+ DH parameter interoperability with primes > 1024 bit

@@ -960,7 +973,12 @@ thus using a custom/suitable length. Example +# Example using a PEM-encoded file. SSLCertificateFile "/usr/local/apache2/conf/ssl.crt/server.crt" +# Example use of a certificate and private key from a PKCS#11 token: +SSLCryptoDevice pkcs11 +... +SSLCertificateFile "pkcs11:token=My%20Token%20Name;id=45" diff --git a/modules/ssl/ssl_engine_config.c b/modules/ssl/ssl_engine_config.c index e1873cdd8e..425e455a57 100644 --- a/modules/ssl/ssl_engine_config.c +++ b/modules/ssl/ssl_engine_config.c @@ -1016,7 +1016,9 @@ const char *ssl_cmd_SSLCertificateFile(cmd_parms *cmd, SSLSrvConfigRec *sc = mySrvConfig(cmd->server); const char *err; - if ((err = ssl_cmd_check_file(cmd, &arg))) { + /* Only check for non-ENGINE based certs. */ + if (!modssl_is_engine_id(arg) + && (err = ssl_cmd_check_file(cmd, &arg))) { return err; } @@ -1033,7 +1035,7 @@ const char *ssl_cmd_SSLCertificateKeyFile(cmd_parms *cmd, const char *err; /* Check keyfile exists for non-ENGINE keys. */ - if (!modssl_is_engine_key(arg) + if (!modssl_is_engine_id(arg) && (err = ssl_cmd_check_file(cmd, &arg))) { return err; } diff --git a/modules/ssl/ssl_engine_init.c b/modules/ssl/ssl_engine_init.c index c80d2358fb..75879326c4 100644 --- a/modules/ssl/ssl_engine_init.c +++ b/modules/ssl/ssl_engine_init.c @@ -1251,13 +1251,17 @@ static apr_status_t ssl_init_server_certs(server_rec *s, const char *)); i++) { EVP_PKEY *pkey; + const char *engine_certfile = NULL; key_id = apr_psprintf(ptemp, "%s:%d", vhost_id, i); ERR_clear_error(); /* first the certificate (public key) */ - if (mctx->cert_chain) { + if (modssl_is_engine_id(certfile)) { + engine_certfile = certfile; + } + else if (mctx->cert_chain) { if ((SSL_CTX_use_certificate_file(mctx->ssl_ctx, certfile, SSL_FILETYPE_PEM) < 1)) { ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(02561) @@ -1286,12 +1290,28 @@ static apr_status_t ssl_init_server_certs(server_rec *s, ERR_clear_error(); - if (modssl_is_engine_key(keyfile)) { + if (modssl_is_engine_id(keyfile)) { apr_status_t rv; - if ((rv = modssl_load_engine_pkey(s, ptemp, keyfile, &pkey))) { + cert = NULL; + + if ((rv = modssl_load_engine_keypair(s, ptemp, engine_certfile, + keyfile, &cert, &pkey))) { return rv; } + + if (cert) { + if (SSL_CTX_use_certificate(mctx->ssl_ctx, cert) < 1) { + ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO() + "Failed to configure engine certificate %s, check %s", + key_id, certfile); + ssl_log_ssl_error(SSLLOG_MARK, APLOG_EMERG, s); + return APR_EGENERAL; + } + + /* SSL_CTX now owns the cert. */ + X509_free(cert); + } if (SSL_CTX_use_PrivateKey(mctx->ssl_ctx, pkey) < 1) { ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(10130) diff --git a/modules/ssl/ssl_engine_pphrase.c b/modules/ssl/ssl_engine_pphrase.c index 4b74cc589e..85982c80c1 100644 --- a/modules/ssl/ssl_engine_pphrase.c +++ b/modules/ssl/ssl_engine_pphrase.c @@ -588,11 +588,11 @@ int ssl_pphrase_Handle_CB(char *buf, int bufsize, int verify, void *srv) } #if defined(HAVE_OPENSSL_ENGINE_H) && defined(HAVE_ENGINE_INIT) -apr_status_t modssl_load_engine_pkey(server_rec *s, apr_pool_t *p, - const char *keyid, EVP_PKEY **ppkey) +apr_status_t modssl_load_engine_keypair(server_rec *s, apr_pool_t *p, + const char *certid, const char *keyid, + X509 **pubkey, EVP_PKEY **privkey) { SSLModConfigRec *mc = myModConfig(s); - EVP_PKEY *pPrivateKey = NULL; ENGINE *e; UI_METHOD *ui_method; @@ -619,17 +619,31 @@ apr_status_t modssl_load_engine_pkey(server_rec *s, apr_pool_t *p, if (APLOGdebug(s)) { ENGINE_ctrl_cmd_string(e, "VERBOSE", NULL, 0); } - - pPrivateKey = ENGINE_load_private_key(e, keyid, ui_method, NULL); - if (pPrivateKey == NULL) { + + if (certid) { + struct { + const char *cert_id; + X509 *cert; + } params = { certid, NULL }; + + if (!ENGINE_ctrl_cmd(e, "LOAD_CERT_CTRL", 0, ¶ms, NULL, 1)) { + ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(10136) + "Init: Unable to get the certificate"); + ssl_log_ssl_error(SSLLOG_MARK, APLOG_EMERG, s); + return ssl_die(s); + } + + *pubkey = params.cert; + } + + *privkey = ENGINE_load_private_key(e, keyid, ui_method, NULL); + if (*privkey == NULL) { ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(10133) "Init: Unable to get the private key"); ssl_log_ssl_error(SSLLOG_MARK, APLOG_EMERG, s); return ssl_die(s); } - *ppkey = pPrivateKey; - ENGINE_free(e); return APR_SUCCESS; diff --git a/modules/ssl/ssl_private.h b/modules/ssl/ssl_private.h index 8836b10093..3a3c51cc3c 100644 --- a/modules/ssl/ssl_private.h +++ b/modules/ssl/ssl_private.h @@ -996,10 +996,13 @@ BOOL ssl_util_vhost_matches(const char *servername, server_rec *s); /** Pass Phrase Support */ apr_status_t ssl_load_encrypted_pkey(server_rec *, apr_pool_t *, int, const char *, apr_array_header_t **); -/* Load private key from the configured ENGINE, returned as **pkey. - * Errors logged on failure. */ -apr_status_t modssl_load_engine_pkey(server_rec *s, apr_pool_t *p, - const char *keyid, EVP_PKEY **ppkey); + +/* Load public and/or private key from the configured ENGINE. Private + * key returned as *pkey. certid can be NULL, in which case *pubkey + * is not altered. Errors logged on failure. */ +apr_status_t modssl_load_engine_keypair(server_rec *s, apr_pool_t *p, + const char *certid, const char *keyid, + X509 **pubkey, EVP_PKEY **privkey); /** Diffie-Hellman Parameter Support */ DH *ssl_dh_GetParamFromFile(const char *); @@ -1105,8 +1108,8 @@ DH *modssl_get_dh_params(unsigned keylen); int modssl_request_is_tls(const request_rec *r, SSLConnRec **sslconn); /* Returns non-zero if the cert/key filename should be handled through - * the configure ENGINE. */ -int modssl_is_engine_key(const char *name); + * the configured ENGINE. */ +int modssl_is_engine_id(const char *name); #if HAVE_VALGRIND extern int ssl_running_on_valgrind; diff --git a/modules/ssl/ssl_util.c b/modules/ssl/ssl_util.c index c8cab33c43..afb54ac70f 100644 --- a/modules/ssl/ssl_util.c +++ b/modules/ssl/ssl_util.c @@ -515,7 +515,7 @@ void ssl_util_thread_setup(apr_pool_t *p) #endif /* #if APR_HAS_THREADS && MODSSL_USE_OPENSSL_PRE_1_1_API */ -int modssl_is_engine_key(const char *name) +int modssl_is_engine_id(const char *name) { #if defined(HAVE_OPENSSL_ENGINE_H) && defined(HAVE_ENGINE_INIT) /* ### Can handle any other special ENGINE key names here? */