From: Joe Orton Date: Thu, 3 May 2018 13:06:46 +0000 (+0000) Subject: mod_ssl: Add support for loading private keys from ENGINEs. Support X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=04294308f698e36fe138fe7984f19bdcacfd4b2b;p=apache mod_ssl: Add support for loading private keys from ENGINEs. Support for PKCS#11 URIs only, and PIN entry is not threaded through SSLPassPhraseDialog config yet. * modules/ssl/ssl_util.c (modssl_is_engine_key): New function. * modules/ssl/ssl_engine_config.c (ssl_cmd_SSLCertificateKeyFile): Use it, skip check for file existence for engine keys. * modules/ssl/ssl_engine_pphrase.c (modssl_load_engine_pkey): New function. * modules/ssl/ssl_engine_init.c (ssl_init_server_certs): For engine keys, load via modssl_load_engine_pkey. Submitted by: Anderson Sasaki , jorton git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@1830819 13f79535-47bb-0310-9956-ffa450edef68 --- diff --git a/CHANGES b/CHANGES index 28db0b3293..ebb68e8472 100644 --- a/CHANGES +++ b/CHANGES @@ -1,6 +1,9 @@ -*- coding: utf-8 -*- Changes with Apache 2.5.1 + *) mod_ssl: Support loading private keys from an OpenSSL engine using a + PKCS#11 URI. [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 could have prevented the server to load, notably on Windows. PR 62308. diff --git a/docs/log-message-tags/next-number b/docs/log-message-tags/next-number index 6adec3be44..5f25bb8e36 100644 --- a/docs/log-message-tags/next-number +++ b/docs/log-message-tags/next-number @@ -1 +1 @@ -10130 +10134 diff --git a/docs/manual/mod/mod_ssl.xml b/docs/manual/mod/mod_ssl.xml index f4e8c34c2c..aa16916095 100644 --- a/docs/manual/mod/mod_ssl.xml +++ b/docs/manual/mod/mod_ssl.xml @@ -969,15 +969,17 @@ SSLCertificateFile "/usr/local/apache2/conf/ssl.crt/server.crt" SSLCertificateKeyFile Server PEM-encoded private key file -SSLCertificateKeyFile file-path +SSLCertificateKeyFile file-path|keyid server config virtual host +keyid available in 2.5.1 and later.

This directive points to the PEM-encoded private key file for the -server. If the contained private key is encrypted, the pass phrase -dialog is forced at startup time.

+server, or the key ID through a configured cryptographic token. If the +contained private key is encrypted, the pass phrase dialog is forced +at startup time.

The directive can be used multiple times (referencing different filenames) @@ -993,9 +995,21 @@ is highly discouraged. If it is used, the certificate files using such an embedded key must be configured after the certificates using a separate key file.

+

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

+ Example +# To use a private key from a PEM-encoded file: SSLCertificateKeyFile "/usr/local/apache2/conf/ssl.key/server.key" +# To use a private key from a PKCS#11 token: +SSLCryptoDevice pkcs11 +... +SSLCertificateKeyFile "pkcs11:token=My%20Token%20Name;id=45"
diff --git a/modules/ssl/ssl_engine_config.c b/modules/ssl/ssl_engine_config.c index 832bb5ff6a..e1873cdd8e 100644 --- a/modules/ssl/ssl_engine_config.c +++ b/modules/ssl/ssl_engine_config.c @@ -1032,7 +1032,9 @@ const char *ssl_cmd_SSLCertificateKeyFile(cmd_parms *cmd, SSLSrvConfigRec *sc = mySrvConfig(cmd->server); const char *err; - if ((err = ssl_cmd_check_file(cmd, &arg))) { + /* Check keyfile exists for non-ENGINE keys. */ + if (!modssl_is_engine_key(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 cbcadc711f..c80d2358fb 100644 --- a/modules/ssl/ssl_engine_init.c +++ b/modules/ssl/ssl_engine_init.c @@ -1250,6 +1250,8 @@ static apr_status_t ssl_init_server_certs(server_rec *s, (certfile = APR_ARRAY_IDX(mctx->pks->cert_files, i, const char *)); i++) { + EVP_PKEY *pkey; + key_id = apr_psprintf(ptemp, "%s:%d", vhost_id, i); ERR_clear_error(); @@ -1284,12 +1286,26 @@ static apr_status_t ssl_init_server_certs(server_rec *s, ERR_clear_error(); - if ((SSL_CTX_use_PrivateKey_file(mctx->ssl_ctx, keyfile, - SSL_FILETYPE_PEM) < 1) && - (ERR_GET_FUNC(ERR_peek_last_error()) - != X509_F_X509_CHECK_PRIVATE_KEY)) { + if (modssl_is_engine_key(keyfile)) { + apr_status_t rv; + + if ((rv = modssl_load_engine_pkey(s, ptemp, keyfile, &pkey))) { + return rv; + } + + if (SSL_CTX_use_PrivateKey(mctx->ssl_ctx, pkey) < 1) { + ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(10130) + "Failed to configure private key %s from engine", + keyfile); + ssl_log_ssl_error(SSLLOG_MARK, APLOG_EMERG, s); + return APR_EGENERAL; + } + } + else if ((SSL_CTX_use_PrivateKey_file(mctx->ssl_ctx, keyfile, + SSL_FILETYPE_PEM) < 1) + && (ERR_GET_FUNC(ERR_peek_last_error()) + != X509_F_X509_CHECK_PRIVATE_KEY)) { ssl_asn1_t *asn1; - EVP_PKEY *pkey; const unsigned char *ptr; ERR_clear_error(); diff --git a/modules/ssl/ssl_engine_pphrase.c b/modules/ssl/ssl_engine_pphrase.c index f28bb8e83c..7658453590 100644 --- a/modules/ssl/ssl_engine_pphrase.c +++ b/modules/ssl/ssl_engine_pphrase.c @@ -600,3 +600,52 @@ int ssl_pphrase_Handle_CB(char *buf, int bufsize, int verify, void *srv) */ return (len); } + +#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) +{ + SSLModConfigRec *mc = myModConfig(s); + EVP_PKEY *pPrivateKey = NULL; + ENGINE *e; + UI_METHOD *ui_method; + + if (!mc->szCryptoDevice) { + ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(10131) + "Init: Cannot load private key `%s' without engine", + keyid); + return ssl_die(s); + } + + /* + * Using the builtin OpenSSL UI only, for now... + */ + ui_method = UI_OpenSSL(); + + if (!(e = ENGINE_by_id(mc->szCryptoDevice))) { + ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(10132) + "Init: Failed to load Crypto Device API `%s'", + mc->szCryptoDevice); + ssl_log_ssl_error(SSLLOG_MARK, APLOG_EMERG, s); + return ssl_die(s); + } + + if (APLOGdebug(s)) { + ENGINE_ctrl_cmd_string(e, "VERBOSE", NULL, 0); + } + + pPrivateKey = ENGINE_load_private_key(e, keyid, ui_method, NULL); + if (pPrivateKey == 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; +} +#endif diff --git a/modules/ssl/ssl_private.h b/modules/ssl/ssl_private.h index c5182469a5..c43556fb02 100644 --- a/modules/ssl/ssl_private.h +++ b/modules/ssl/ssl_private.h @@ -996,6 +996,10 @@ 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); /** Diffie-Hellman Parameter Support */ DH *ssl_dh_GetParamFromFile(const char *); @@ -1101,6 +1105,10 @@ DH *modssl_get_dh_params(unsigned keylen); * corresponding SSLConnRec structure for the connection. */ 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); + #if HAVE_VALGRIND extern int ssl_running_on_valgrind; #endif diff --git a/modules/ssl/ssl_util.c b/modules/ssl/ssl_util.c index 098ae6a337..ebc3605129 100644 --- a/modules/ssl/ssl_util.c +++ b/modules/ssl/ssl_util.c @@ -522,3 +522,13 @@ 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) +{ +#if defined(HAVE_OPENSSL_ENGINE_H) && defined(HAVE_ENGINE_INIT) + /* ### Can handle any other special ENGINE key names here? */ + return strncmp(name, "pkcs11:", 7) == 0; +#else + return 0; +#endif +}