Hi, This patch leverages PKCS#11 support in nginx http module using libp11. This allows the private key to be stored in a dedicated hardware (or software) component.
The following patch does not deal with the "configure" tools of nginx. I wanted to get feedback prior to writing nginx "autoconf" scripts to deal with multiple platforms. To test, apply the patch, run configure (with http/ssl enabled), and modify objs/Makefile to add "-lp11" to link the libp11 library. To configure use the following parameters: * ssl_pkcs11, on or off * ssl_certificate, no change the server certificate is fetched on the disk * ssl_certificate_key, string mapped to the PKCS#11 "label" attribute * ssl_pkcs11_pin, string of the token PIN * ssl_pkcs11_module, path to the PKCS#11 shared library Instead of tweaking ngx_ssl_certificate function, I have added the ngx_ssl_certificate_pkcs11 function which is used when ssl_pkcs11 is enabled. This approach could also be applied to the nginx mail module. Feedback appreciated. Regards, -- Cordialement, Thomas Calderon Laboratoire architectures matérielles et logicielles Sous-direction expertise ANSSI Tél: 01 71 75 88 55
# HG changeset patch # User Thomas Calderon <thomas.calde...@ssi.gouv.fr> # Date 1415031852 -3600 # Mon Nov 03 17:24:12 2014 +0100 # Node ID b42ce1de67d6ef5b434ee38a5c1adac9be7b4e9e # Parent dff86e2246a53b0f4a61935cd5c8c0a0f66d0ca2 Add PKCS#11 support to nginx http module. This patch leverages PKCS#11 support in nginx http module using libp11. This allows the private key to be stored in a dedicated hardware (or software) component. To configure use the following parameters: * ssl_pkcs11, on or off * ssl_certificate, no change the server certificate is fetched on the disk * ssl_certificate_key, string mapped to the PKCS#11 "label" attribute * ssl_pkcs11_pin, string of the token PIN * ssl_pkcs11_module, path to the PKCS#11 shared library Instead of tweaking the ngx_ssl_certificate function, I have added another function (ngx_ssl_certificate_pkcs11) which is called when ssl_pkcs11 is enabled. diff -r dff86e2246a5 -r b42ce1de67d6 src/event/ngx_event_openssl.c --- a/src/event/ngx_event_openssl.c Mon Aug 25 13:41:31 2014 +0400 +++ b/src/event/ngx_event_openssl.c Mon Nov 03 17:24:12 2014 +0100 @@ -189,6 +189,197 @@ ngx_int_t +ngx_ssl_certificate_pkcs11(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *cert, + ngx_str_t *confkey, ngx_str_t *pkcs11_module, ngx_str_t *pkcs11_pin) +{ + BIO *bio; + X509 *x509; + u_long n; + + PKCS11_KEY *keys; + EVP_PKEY *pkey = NULL; + PKCS11_SLOT *slot; + unsigned int num_keys; + unsigned int i; + + if (ngx_conf_full_name(cf->cycle, cert, 1) != NGX_OK) { + return NGX_ERROR; + } + + /* + * we can't use SSL_CTX_use_certificate_chain_file() as it doesn't + * allow to access certificate later from SSL_CTX, so we reimplement + * it here + */ + + bio = BIO_new_file((char *) cert->data, "r"); + if (bio == NULL) { + ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0, + "BIO_new_file(\"%s\") failed", cert->data); + return NGX_ERROR; + } + + x509 = PEM_read_bio_X509_AUX(bio, NULL, NULL, NULL); + if (x509 == NULL) { + ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0, + "PEM_read_bio_X509_AUX(\"%s\") failed", cert->data); + BIO_free(bio); + return NGX_ERROR; + } + + if (SSL_CTX_use_certificate(ssl->ctx, x509) == 0) { + ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0, + "SSL_CTX_use_certificate(\"%s\") failed", cert->data); + X509_free(x509); + BIO_free(bio); + return NGX_ERROR; + } + + if (SSL_CTX_set_ex_data(ssl->ctx, ngx_ssl_certificate_index, x509) + == 0) + { + ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0, + "SSL_CTX_set_ex_data() failed"); + return NGX_ERROR; + } + + X509_free(x509); + + /* read rest of the chain */ + + for ( ;; ) { + + x509 = PEM_read_bio_X509(bio, NULL, NULL, NULL); + if (x509 == NULL) { + n = ERR_peek_last_error(); + + if (ERR_GET_LIB(n) == ERR_LIB_PEM + && ERR_GET_REASON(n) == PEM_R_NO_START_LINE) + { + /* end of file */ + ERR_clear_error(); + break; + } + + /* some real error */ + + ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0, + "PEM_read_bio_X509(\"%s\") failed", cert->data); + BIO_free(bio); + return NGX_ERROR; + } + + if (SSL_CTX_add_extra_chain_cert(ssl->ctx, x509) == 0) { + ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0, + "SSL_CTX_add_extra_chain_cert(\"%s\") failed", + cert->data); + X509_free(x509); + BIO_free(bio); + return NGX_ERROR; + } + } + + BIO_free(bio); + + /* Build the path to PKCS#11 module (add build prefix if no + * absolute path given. + */ + if (ngx_conf_full_name(cf->cycle, pkcs11_module, 1) != NGX_OK) + { + return NGX_ERROR; + } + + /* Start the libp11 dance */ + ssl->pkcs11_ctx = PKCS11_CTX_new(); + if(!(ssl->pkcs11_ctx)) + { + ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0, + "PKCS11_CTX_new failed"); + return NGX_ERROR; + } + + if(PKCS11_CTX_load(ssl->pkcs11_ctx, (const char *)pkcs11_module->data) != 0) + { + ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0, + "PKCS11_CTX_load(\"%s\") failed", pkcs11_module->data); + return NGX_ERROR; + } + + if(PKCS11_enumerate_slots(ssl->pkcs11_ctx, &ssl->slots, &ssl->nslots) != 0) + { + ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0, + "PKCS11_enumerate_slots failed"); + return NGX_ERROR; + } + + /* Find the first slot with a token */ + slot = PKCS11_find_token(ssl->pkcs11_ctx, ssl->slots, ssl->nslots); + if(!slot) + { + ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0, + "PKCS11_find_token failed"); + return NGX_ERROR; + } + + /* 0 means RO session */ + if(PKCS11_open_session(slot, 0) != 0) + { + ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0, + "PKCS11_open_session failed"); + return NGX_ERROR; + } + + /* 0 means USER login */ + if(PKCS11_login(slot, 0, (const char *)pkcs11_pin->data) !=0) + { + ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0, + "PKCS11_login failed"); + return NGX_ERROR; + } + + /* We have to enumerate all key, we cannot filter them */ + if(PKCS11_enumerate_keys(slot->token, &keys, &num_keys) !=0) + { + ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0, + "PKCS11_enumerate_keys failed"); + return NGX_ERROR; + } + + /* Let's try to find a matching key */ + for(i=0; i < num_keys; i++){ + if(PKCS11_get_key_type(&keys[i]) == EVP_PKEY_RSA) + { + /* check that required label match, assume LABEL is at mot 64 */ + if(memcmp(confkey->data, keys[i].label, + strnlen((const char *)confkey->data, 64)) == 0) + { + pkey = PKCS11_get_private_key(&keys[i]); + } + break; + } + } + + /* Did we find a matching key ? */ + if(!pkey){ + ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0, + "Could not find a PKCS11 label key matching (\"%s\")", confkey->data); + return NGX_ERROR; + } + + /* Use the retrieved EVP_PKEY from libp11 */ + if (SSL_CTX_use_PrivateKey(ssl->ctx, pkey) + == 0) + { + ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0, + "SSL_CTX_use_PrivateKey(\"%s\") failed", confkey->data); + return NGX_ERROR; + } + + return NGX_OK; +} + + +ngx_int_t ngx_ssl_create(ngx_ssl_t *ssl, ngx_uint_t protocols, void *data) { ssl->ctx = SSL_CTX_new(SSLv23_method()); @@ -2839,6 +3030,12 @@ ngx_ssl_cleanup_ctx(void *data) { ngx_ssl_t *ssl = data; + if(ssl->pkcs11_ctx) + { + PKCS11_release_all_slots(ssl->pkcs11_ctx, ssl->slots, ssl->nslots); + PKCS11_CTX_unload(ssl->pkcs11_ctx); + PKCS11_CTX_free(ssl->pkcs11_ctx); + } SSL_CTX_free(ssl->ctx); } diff -r dff86e2246a5 -r b42ce1de67d6 src/event/ngx_event_openssl.h --- a/src/event/ngx_event_openssl.h Mon Aug 25 13:41:31 2014 +0400 +++ b/src/event/ngx_event_openssl.h Mon Nov 03 17:24:12 2014 +0100 @@ -32,6 +32,8 @@ #define NGX_SSL_NAME "OpenSSL" +#include <libp11.h> + #define ngx_ssl_session_t SSL_SESSION #define ngx_ssl_conn_t SSL @@ -39,6 +41,9 @@ typedef struct { SSL_CTX *ctx; + PKCS11_CTX *pkcs11_ctx; + PKCS11_SLOT *slots; + unsigned int nslots; ngx_log_t *log; size_t buffer_size; } ngx_ssl_t; @@ -124,6 +129,9 @@ ngx_int_t ngx_ssl_create(ngx_ssl_t *ssl, ngx_uint_t protocols, void *data); ngx_int_t ngx_ssl_certificate(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *cert, ngx_str_t *key, ngx_array_t *passwords); +ngx_int_t ngx_ssl_certificate_pkcs11(ngx_conf_t *cf, ngx_ssl_t *ssl, + ngx_str_t *cert, ngx_str_t *key, ngx_str_t *pkcs11_module, + ngx_str_t *pkcs11_pin); ngx_int_t ngx_ssl_client_certificate(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *cert, ngx_int_t depth); ngx_int_t ngx_ssl_trusted_certificate(ngx_conf_t *cf, ngx_ssl_t *ssl, diff -r dff86e2246a5 -r b42ce1de67d6 src/http/modules/ngx_http_ssl_module.c --- a/src/http/modules/ngx_http_ssl_module.c Mon Aug 25 13:41:31 2014 +0400 +++ b/src/http/modules/ngx_http_ssl_module.c Mon Nov 03 17:24:12 2014 +0100 @@ -79,6 +79,27 @@ offsetof(ngx_http_ssl_srv_conf_t, enable), NULL }, + { ngx_string("ssl_pkcs11"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_FLAG, + ngx_http_ssl_enable, + NGX_HTTP_SRV_CONF_OFFSET, + offsetof(ngx_http_ssl_srv_conf_t, enable_pkcs11), + NULL }, + + { ngx_string("ssl_pkcs11_module"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1, + ngx_conf_set_str_slot, + NGX_HTTP_SRV_CONF_OFFSET, + offsetof(ngx_http_ssl_srv_conf_t, pkcs11_module), + NULL }, + + { ngx_string("ssl_pkcs11_pin"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1, + ngx_conf_set_str_slot, + NGX_HTTP_SRV_CONF_OFFSET, + offsetof(ngx_http_ssl_srv_conf_t, pkcs11_pin), + NULL }, + { ngx_string("ssl_certificate"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1, ngx_conf_set_str_slot, @@ -505,6 +526,8 @@ * set by ngx_pcalloc(): * * sscf->protocols = 0; + * sscf->pkcs11_module = { 0, NULL }; + * sscf->pkcs11_pin = { 0, NULL }; * sscf->certificate = { 0, NULL }; * sscf->certificate_key = { 0, NULL }; * sscf->dhparam = { 0, NULL }; @@ -519,6 +542,7 @@ */ sscf->enable = NGX_CONF_UNSET; + sscf->enable_pkcs11 = NGX_CONF_UNSET; sscf->prefer_server_ciphers = NGX_CONF_UNSET; sscf->buffer_size = NGX_CONF_UNSET_SIZE; sscf->verify = NGX_CONF_UNSET_UINT; @@ -628,6 +652,25 @@ } } + if (conf->enable_pkcs11) { + + if (conf->pkcs11_module.len == 0) { + ngx_log_error(NGX_LOG_EMERG, cf->log, 0, + "no \"ssl_pkcs11_module\" is defined for " + "the \"ssl_pkcs11\" directive in %s:%ui", + conf->file, conf->line); + return NGX_CONF_ERROR; + } + + if (conf->pkcs11_pin.len == 0) { + ngx_log_error(NGX_LOG_EMERG, cf->log, 0, + "no \"ssl_pkcs11_pin\" is defined for " + "the \"ssl_pkcs11\" directive in %s:%ui", + conf->file, conf->line); + return NGX_CONF_ERROR; + } + } + if (ngx_ssl_create(&conf->ssl, conf->protocols, conf) != NGX_OK) { return NGX_CONF_ERROR; } @@ -663,12 +706,26 @@ cln->handler = ngx_ssl_cleanup_ctx; cln->data = &conf->ssl; - if (ngx_ssl_certificate(cf, &conf->ssl, &conf->certificate, - &conf->certificate_key, conf->passwords) - != NGX_OK) + if(conf->enable && conf->enable_pkcs11) { - return NGX_CONF_ERROR; + if (ngx_ssl_certificate_pkcs11(cf, &conf->ssl, &conf->certificate, + &conf->certificate_key, &conf->pkcs11_module, + &conf->pkcs11_pin) + != NGX_OK) + { + return NGX_CONF_ERROR; + } } + else + { + if (ngx_ssl_certificate(cf, &conf->ssl, &conf->certificate, + &conf->certificate_key, conf->passwords) + != NGX_OK) + { + return NGX_CONF_ERROR; + } + } + if (SSL_CTX_set_cipher_list(conf->ssl.ctx, (const char *) conf->ciphers.data) diff -r dff86e2246a5 -r b42ce1de67d6 src/http/modules/ngx_http_ssl_module.h --- a/src/http/modules/ngx_http_ssl_module.h Mon Aug 25 13:41:31 2014 +0400 +++ b/src/http/modules/ngx_http_ssl_module.h Mon Nov 03 17:24:12 2014 +0100 @@ -16,6 +16,7 @@ typedef struct { ngx_flag_t enable; + ngx_flag_t enable_pkcs11; ngx_ssl_t ssl; @@ -32,6 +33,8 @@ time_t session_timeout; + ngx_str_t pkcs11_module; + ngx_str_t pkcs11_pin; ngx_str_t certificate; ngx_str_t certificate_key; ngx_str_t dhparam;
_______________________________________________ nginx-devel mailing list nginx-devel@nginx.org http://mailman.nginx.org/mailman/listinfo/nginx-devel