On 06/02/13 17:24, Primoz Bratanic wrote:
Hi,
Apache supports specifying multiple certificates (different types) for same
host in line with OpenSSL support (RSA, DSA, ECC). This allows using ECC key
exchange methods with clients that support it and it's backwards compatible.
I wonder how much work would it be to add support for this to nginx. Is it
just allowing specifying 2-3 certificates (and checking they have different
key type) + adding support for returning proper key chain or are the any
other obvious roadblocks (that are not obvious to me).
Here's a first stab at a patch. I hope this is a useful starting point
for getting this feature added to Nginx.
To specify an RSA cert plus an ECC cert, use...
ssl_certificate my_rsa.crt my_ecc.crt;
ssl_certificate_key my_rsa.key my_ecc.key;
ssl_prefer_server_ciphers on;
Also, configure ssl_ciphers to prefer at least 1 ECDSA cipher and permit
at least 1 RSA cipher.
I think DSA certs should work too, but I've not tested this.
Issues I'm aware of with this patch:
- It doesn't check that each of the certs has a different key type
(but perhaps it should). If you specify multiple certs with the same
algorithm, all but the last one will be ignored.
- The certs and keys need to be specified in the correct order. If
you specify "my_rsa.crt my_ecc.crt" and "my_ecc.key my_rsa.key", Nginx
will start but it won't be able to complete any SSL handshakes. This
could be improved.
- It doesn't add the new feature to mail_ssl_module. Perhaps it should.
- The changes I made to ngx_conf_set_str_array_slot() work for me,
but do they break anything?
- An RSA cert and an ECC cert might well be issued by different CAs.
On Apache httpd, you have to use SSLCACertificatePath to persuade
OpenSSL to send different Intermediate certs for each one.
Nginx doesn't currently have an equivalent directive, and Maxim has
previously said it's unlikely to be added [1].
I haven't researched this properly yet, but I think it might be possible
to do "certificate path" in memory (i.e. without syscalls and disk
access on each certificate check) using the OpenSSL X509_LOOKUP API.
- I expect Maxim will have other comments. :-)
[1] http://forum.nginx.org/read.php?2,229129,229151
--
Rob Stradling
Senior Research & Development Scientist
COMODO - Creating Trust Online
diff --git a/src/core/ngx_conf_file.c b/src/core/ngx_conf_file.c
--- a/src/core/ngx_conf_file.c
+++ b/src/core/ngx_conf_file.c
@@ -1003,39 +1003,42 @@ ngx_conf_set_str_slot(ngx_conf_t *cf, ng
}
char *
ngx_conf_set_str_array_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
char *p = conf;
- ngx_str_t *value, *s;
+ ngx_str_t *value, *s = NULL;
+ ngx_uint_t i;
ngx_array_t **a;
- ngx_conf_post_t *post;
+ ngx_conf_post_t *post = NULL;
a = (ngx_array_t **) (p + cmd->offset);
if (*a == NGX_CONF_UNSET_PTR) {
*a = ngx_array_create(cf->pool, 4, sizeof(ngx_str_t));
if (*a == NULL) {
return NGX_CONF_ERROR;
}
}
- s = ngx_array_push(*a);
- if (s == NULL) {
- return NGX_CONF_ERROR;
+ value = cf->args->elts;
+
+ for (i = 1; i < cf->args->nelts; i++) {
+ s = ngx_array_push(*a);
+ if (s == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ *s = value[i];
}
- value = cf->args->elts;
-
- *s = value[1];
-
- if (cmd->post) {
+ if ((cmd->post) && s) {
post = cmd->post;
return post->post_handler(cf, post, s);
}
return NGX_CONF_OK;
}
diff --git a/src/event/ngx_event_openssl.c b/src/event/ngx_event_openssl.c
--- a/src/event/ngx_event_openssl.c
+++ b/src/event/ngx_event_openssl.c
@@ -200,16 +200,20 @@ ngx_ssl_create(ngx_ssl_t *ssl, ngx_uint_
SSL_CTX_set_options(ssl->ctx, SSL_OP_SSLREF2_REUSE_CERT_TYPE_BUG);
SSL_CTX_set_options(ssl->ctx, SSL_OP_MICROSOFT_BIG_SSLV3_BUFFER);
#ifdef SSL_OP_MSIE_SSLV2_RSA_PADDING
/* this option allow a potential SSL 2.0 rollback (CAN-2005-2969) */
SSL_CTX_set_options(ssl->ctx, SSL_OP_MSIE_SSLV2_RSA_PADDING);
#endif
+#ifdef SSL_OP_SAFARI_ECDHE_ECDSA_BUG
+ SSL_CTX_set_options(ssl->ctx, SSL_OP_SAFARI_ECDHE_ECDSA_BUG);
+#endif
+
SSL_CTX_set_options(ssl->ctx, SSL_OP_SSLEAY_080_CLIENT_DH_BUG);
SSL_CTX_set_options(ssl->ctx, SSL_OP_TLS_D5_BUG);
SSL_CTX_set_options(ssl->ctx, SSL_OP_TLS_BLOCK_PADDING_BUG);
SSL_CTX_set_options(ssl->ctx, SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS);
SSL_CTX_set_options(ssl->ctx, SSL_OP_SINGLE_DH_USE);
@@ -351,16 +355,35 @@ ngx_ssl_certificate(ngx_conf_t *cf, ngx_
return NGX_ERROR;
}
return NGX_OK;
}
ngx_int_t
+ngx_ssl_certificates(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_array_t *certs,
+ ngx_array_t *keys)
+{
+ ngx_uint_t i;
+ ngx_str_t *cert;
+ ngx_str_t *key;
+
+ cert = certs->elts;
+ key = keys->elts;
+
+ for (i = 0; i < certs->nelts; i++)
+ if (ngx_ssl_certificate(cf, ssl, &cert[i], &key[i]) != NGX_OK)
+ return NGX_ERROR;
+
+ return NGX_OK;
+}
+
+
+ngx_int_t
ngx_ssl_client_certificate(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *cert,
ngx_int_t depth)
{
STACK_OF(X509_NAME) *list;
SSL_CTX_set_verify(ssl->ctx, SSL_VERIFY_PEER, ngx_ssl_verify_callback);
SSL_CTX_set_verify_depth(ssl->ctx, depth);
diff --git a/src/event/ngx_event_openssl.h b/src/event/ngx_event_openssl.h
--- a/src/event/ngx_event_openssl.h
+++ b/src/event/ngx_event_openssl.h
@@ -104,18 +104,18 @@ typedef struct {
#define NGX_SSL_BUFFER 1
#define NGX_SSL_CLIENT 2
#define NGX_SSL_BUFSIZE 16384
ngx_int_t ngx_ssl_init(ngx_log_t *log);
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_int_t ngx_ssl_certificates(ngx_conf_t *cf, ngx_ssl_t *ssl,
+ ngx_array_t *certs, ngx_array_t *keys);
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,
ngx_str_t *cert, ngx_int_t depth);
ngx_int_t ngx_ssl_crl(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *crl);
ngx_int_t ngx_ssl_stapling(ngx_conf_t *cf, ngx_ssl_t *ssl,
ngx_str_t *file, ngx_str_t *responder, ngx_uint_t verify);
ngx_int_t ngx_ssl_stapling_resolver(ngx_conf_t *cf, ngx_ssl_t *ssl,
diff --git a/src/http/modules/ngx_http_ssl_module.c b/src/http/modules/ngx_http_ssl_module.c
--- a/src/http/modules/ngx_http_ssl_module.c
+++ b/src/http/modules/ngx_http_ssl_module.c
@@ -65,27 +65,27 @@ static ngx_command_t ngx_http_ssl_comma
{ ngx_string("ssl"),
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),
NULL },
{ ngx_string("ssl_certificate"),
- NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,
- ngx_conf_set_str_slot,
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_1MORE,
+ ngx_conf_set_str_array_slot,
NGX_HTTP_SRV_CONF_OFFSET,
- offsetof(ngx_http_ssl_srv_conf_t, certificate),
+ offsetof(ngx_http_ssl_srv_conf_t, certificates),
NULL },
{ ngx_string("ssl_certificate_key"),
- NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,
- ngx_conf_set_str_slot,
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_1MORE,
+ ngx_conf_set_str_array_slot,
NGX_HTTP_SRV_CONF_OFFSET,
- offsetof(ngx_http_ssl_srv_conf_t, certificate_key),
+ offsetof(ngx_http_ssl_srv_conf_t, certificate_keys),
NULL },
{ ngx_string("ssl_dhparam"),
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, dhparam),
NULL },
@@ -404,18 +404,16 @@ ngx_http_ssl_create_srv_conf(ngx_conf_t
if (sscf == NULL) {
return NULL;
}
/*
* set by ngx_pcalloc():
*
* sscf->protocols = 0;
- * sscf->certificate = { 0, NULL };
- * sscf->certificate_key = { 0, NULL };
* sscf->dhparam = { 0, NULL };
* sscf->ecdh_curve = { 0, NULL };
* sscf->client_certificate = { 0, NULL };
* sscf->trusted_certificate = { 0, NULL };
* sscf->crl = { 0, NULL };
* sscf->ciphers = { 0, NULL };
* sscf->shm_zone = NULL;
* sscf->stapling_file = { 0, NULL };
@@ -423,16 +421,18 @@ ngx_http_ssl_create_srv_conf(ngx_conf_t
*/
sscf->enable = NGX_CONF_UNSET;
sscf->prefer_server_ciphers = NGX_CONF_UNSET;
sscf->verify = NGX_CONF_UNSET_UINT;
sscf->verify_depth = NGX_CONF_UNSET_UINT;
sscf->builtin_session_cache = NGX_CONF_UNSET;
sscf->session_timeout = NGX_CONF_UNSET;
+ sscf->certificates = NGX_CONF_UNSET_PTR;
+ sscf->certificate_keys = NGX_CONF_UNSET_PTR;
sscf->session_ticket_keys = NGX_CONF_UNSET_PTR;
sscf->stapling = NGX_CONF_UNSET;
sscf->stapling_verify = NGX_CONF_UNSET;
return sscf;
}
@@ -463,18 +463,20 @@ ngx_http_ssl_merge_srv_conf(ngx_conf_t *
ngx_conf_merge_bitmask_value(conf->protocols, prev->protocols,
(NGX_CONF_BITMASK_SET|NGX_SSL_SSLv3|NGX_SSL_TLSv1
|NGX_SSL_TLSv1_1|NGX_SSL_TLSv1_2));
ngx_conf_merge_uint_value(conf->verify, prev->verify, 0);
ngx_conf_merge_uint_value(conf->verify_depth, prev->verify_depth, 1);
- ngx_conf_merge_str_value(conf->certificate, prev->certificate, "");
- ngx_conf_merge_str_value(conf->certificate_key, prev->certificate_key, "");
+ ngx_conf_merge_ptr_value(conf->certificates, prev->certificates,
+ NGX_CONF_UNSET_PTR);
+ ngx_conf_merge_ptr_value(conf->certificate_keys, prev->certificate_keys,
+ NGX_CONF_UNSET_PTR);
ngx_conf_merge_str_value(conf->dhparam, prev->dhparam, "");
ngx_conf_merge_str_value(conf->client_certificate, prev->client_certificate,
"");
ngx_conf_merge_str_value(conf->trusted_certificate,
prev->trusted_certificate, "");
ngx_conf_merge_str_value(conf->crl, prev->crl, "");
@@ -489,44 +491,42 @@ ngx_http_ssl_merge_srv_conf(ngx_conf_t *
ngx_conf_merge_str_value(conf->stapling_file, prev->stapling_file, "");
ngx_conf_merge_str_value(conf->stapling_responder,
prev->stapling_responder, "");
conf->ssl.log = cf->log;
if (conf->enable) {
- if (conf->certificate.len == 0) {
+ if ((conf->certificates == NGX_CONF_UNSET_PTR)
+ || (conf->certificates->nelts == 0)) {
ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
"no \"ssl_certificate\" is defined for "
"the \"ssl\" directive in %s:%ui",
conf->file, conf->line);
return NGX_CONF_ERROR;
}
- if (conf->certificate_key.len == 0) {
- ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
- "no \"ssl_certificate_key\" is defined for "
- "the \"ssl\" directive in %s:%ui",
- conf->file, conf->line);
- return NGX_CONF_ERROR;
- }
-
} else {
- if (conf->certificate.len == 0) {
+ if ((conf->certificates == NGX_CONF_UNSET_PTR)
+ || (conf->certificates->nelts == 0)) {
return NGX_CONF_OK;
}
- if (conf->certificate_key.len == 0) {
- ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
- "no \"ssl_certificate_key\" is defined "
- "for certificate \"%V\"", &conf->certificate);
- return NGX_CONF_ERROR;
- }
+ }
+
+ if ((conf->certificate_keys == NGX_CONF_UNSET_PTR)
+ || (conf->certificate_keys->nelts < conf->certificates->nelts)) {
+ ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
+ "no \"ssl_certificate_key\" is defined "
+ "for certificate \"%V\"",
+ &((ngx_str_t*)conf->certificates->elts)
+ [conf->certificate_keys->nelts]);
+ return NGX_CONF_ERROR;
}
if (ngx_ssl_create(&conf->ssl, conf->protocols, conf) != NGX_OK) {
return NGX_CONF_ERROR;
}
#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
@@ -550,18 +550,18 @@ ngx_http_ssl_merge_srv_conf(ngx_conf_t *
cln = ngx_pool_cleanup_add(cf->pool, 0);
if (cln == NULL) {
return NGX_CONF_ERROR;
}
cln->handler = ngx_ssl_cleanup_ctx;
cln->data = &conf->ssl;
- if (ngx_ssl_certificate(cf, &conf->ssl, &conf->certificate,
- &conf->certificate_key)
+ if (ngx_ssl_certificates(cf, &conf->ssl, conf->certificates,
+ conf->certificate_keys)
!= NGX_OK)
{
return NGX_CONF_ERROR;
}
if (SSL_CTX_set_cipher_list(conf->ssl.ctx,
(const char *) conf->ciphers.data)
== 0)
diff --git a/src/http/modules/ngx_http_ssl_module.h b/src/http/modules/ngx_http_ssl_module.h
--- a/src/http/modules/ngx_http_ssl_module.h
+++ b/src/http/modules/ngx_http_ssl_module.h
@@ -25,18 +25,18 @@ typedef struct {
ngx_uint_t verify;
ngx_uint_t verify_depth;
ssize_t builtin_session_cache;
time_t session_timeout;
- ngx_str_t certificate;
- ngx_str_t certificate_key;
+ ngx_array_t *certificates;
+ ngx_array_t *certificate_keys;
ngx_str_t dhparam;
ngx_str_t ecdh_curve;
ngx_str_t client_certificate;
ngx_str_t trusted_certificate;
ngx_str_t crl;
ngx_str_t ciphers;
_______________________________________________
nginx-devel mailing list
nginx-devel@nginx.org
http://mailman.nginx.org/mailman/listinfo/nginx-devel