Opened 3 years ago

Closed 3 years ago

Last modified 2 years ago

#814 closed enhancement (fixed)

Support for parallel ECDSA / RSA certificates

Reported by: jsha@… Owned by:
Priority: minor Milestone:
Component: nginx-core Version: 1.9.x
Keywords: Cc:
uname -a: N/A
nginx -V: 1.9.2


I'd like to be able to deploy an ECDSA certificate alongside an RSA certificate, and have Nginx choose which certificate to serve based on the signature_algorithms extension in the handshake:

ECDSA certificates are much smaller, and so allow faster handshake times on limited bandwidth. But they are not yet broadly supported. Allowing deployment of dual certificates on Nginx would help greatly with a gradual rollout of ECDSA certificates, which would in turn help client adoption and promote a faster Internet.

Change History (13)

comment:1 Changed 3 years ago by ignisf@…

There were patches for this that made the rounds at some point in time, have not heard anything about it since then. I wonder what happened.

comment:2 Changed 3 years ago by…

As acclimed Apache httpd 2.4 has dual certificate support:

comment:3 Changed 3 years ago by david.sardari@…

Simultaneous usage of RSA and ECDSA certificates would be perfect. That way, you can request Let's Encrypt certificates for both types and use them with priority of ECDSA ciphers over RSA ciphers in order to keep the server load down. Therefore, I was thinking about using Apache, but turned the idea down, because Nginx is easier to configure for me and more lightweight. Hopefully, Nginx will support this in the near future.

Last edited 3 years ago by david.sardari@… (previous) (diff)

comment:4 Changed 3 years ago by Sergey Kandaurov <pluknet@…>

In 930:183a6b1f3fa5/nginx-tests:

Tests: http ssl tests with multiple certificates (ticket #814).

comment:5 Changed 3 years ago by Maxim Dounin <mdounin@…>

In 6550:51e1f047d15d/nginx:

SSL: support for multiple certificates (ticket #814).

comment:6 Changed 3 years ago by mdounin

  • Resolution set to fixed
  • Status changed from new to closed

Support for multiple certificates was committed and will be available in nginx 1.11.0.

comment:7 follow-up: Changed 3 years ago by

I've installed 1.11.0 and there seems to be some issue around OCSP stapling when specifying multiple ssl_certificate and ssl_certificate_key directives. Has anyone experienced any issues?

comment:8 in reply to: ↑ 7 Changed 3 years ago by mdounin

There are two things to consider when using multiple certificates:

  • Only OpenSSL 1.0.2+ supports separate chains for different certificates. With older versions, things will only work properly if chains are exactly the same for all certificates used (or there are no chains at all).
  • OCSP stapling won't work properly with multiple certificates with OpenSSL versions before 1.0.1d+, 1.0.0k, 0.9.8y+ (see a2d5d45f1525). This is due to a bug in OpenSSL fixed in the versions specified.

Check nginx -V output to find out the version of the OpenSSL library you are using.

comment:9 Changed 2 years ago by david.sardari@…

I got my RSA and ECDSA certificates from Let's Encrypt. So, both intermediate certificates are identical. Do I have to append the intermediate certificate to both certificates? e.g.:

cat intermediate.pem > rsa_chain.pem
cat intermediate.pem > ecdsa_chain.pem

Doing it that way leads to an error in ssllabs:

Certificates provided	3 (3663 bytes)
Chain issues	Incorrect order, Extra certs

comment:10 Changed 2 years ago by mdounin

The error reported suggests that you are using an old OpenSSL (before 1.0.2), so you have to append the chain to only one of the certificates (it will be used for both).

comment:11 Changed 2 years ago by david.sardari@…

Some additional info about my Gentoo Linux Nginx server:

netcup88 ~ # nginx -V
nginx version: nginx/1.11.1
built with LibreSSL 2.4.1
TLS SNI support enabled
configure arguments: --prefix=/usr --conf-path=/etc/nginx/nginx.conf --error-log-path=/var/log/nginx/error_log --pid-path=/run/ --lock-path=/run/lock/nginx.lock --with-cc-opt=-I/usr/include --with-ld-opt=-L/usr/lib --http-log-path=/var/log/nginx/access_log --http-client-body-temp-path=/var/lib/nginx/tmp/client --http-proxy-temp-path=/var/lib/nginx/tmp/proxy --http-fastcgi-temp-path=/var/lib/nginx/tmp/fastcgi --http-scgi-temp-path=/var/lib/nginx/tmp/scgi --http-uwsgi-temp-path=/var/lib/nginx/tmp/uwsgi --with-http_v2_module --with-ipv6 --with-pcre --without-http_autoindex_module --without-http_browser_module --without-http_charset_module --without-http_empty_gif_module --without-http_fastcgi_module --without-http_geo_module --without-http_gzip_module --without-http_limit_req_module --without-http_limit_conn_module --without-http_map_module --without-http_memcached_module --without-http_scgi_module --without-http_ssi_module --without-http_split_clients_module --without-http_upstream_ip_hash_module --without-http_userid_module --without-http_uwsgi_module --with-http_realip_module --add-module=external_module/headers-more-nginx-module-0.30 --with-http_ssl_module --without-stream_upstream_hash_module --without-stream_upstream_least_conn_module --without-stream_upstream_zone_module --without-stream_upstream_hash_module --without-stream_upstream_least_conn_module --without-stream_upstream_zone_module --without-stream_upstream_hash_module --without-stream_upstream_least_conn_module --without-stream_upstream_zone_module --without-mail_imap_module --without-mail_pop3_module --without-mail_smtp_module --user='nginx --group=nginx'
netcup88 ~ # cat /etc/nginx/nginx.conf 
user nginx nginx;
worker_processes auto;

events {
	worker_connections 1024;
	use epoll;

http {
	include /etc/nginx/mime.types;
	default_type application/octet-stream;

	include /etc/nginx/conf.d/http_ssl.conf;
	include /etc/nginx/conf.d/http_harden.conf;
	include /etc/nginx/pages/*.conf;

	server {
		listen 80;
		listen [::]:80;
		listen 443 ssl http2;
		listen [::]:443 ssl http2;

		return 444;
david@netcup88 ~ $ cat /etc/nginx/conf.d/http_ssl.conf 
# certs and private keys
ssl_certificate		/etc/nginx/ssl/ecdsa.pem;
ssl_certificate_key	/etc/nginx/ssl/ecdsa.key;
ssl_certificate		/etc/nginx/ssl/rsa.pem;
ssl_certificate_key	/etc/nginx/ssl/rsa.key;

# OCSP Stapling
ssl_stapling		on;

# protocols and ciphers
ssl_ecdh_curve		prime256v1;
ssl_prefer_server_ciphers on;
ssl_protocols		TLSv1 TLSv1.1 TLSv1.2;

# sessions
ssl_session_cache	shared:SSL:50m;
ssl_session_tickets	off;
ssl_session_timeout	1d;
Last edited 2 years ago by david.sardari@… (previous) (diff)

comment:12 Changed 2 years ago by david.sardari@…

And some additional info. As you can see both ECDSA and RSA based ciphers are offered, which is only the case if LibreSSL has access to key pairs of both types. The Nginx's docs do not explain the correct way to include the intermediate certificate if both RSA and ECDSA is in use. If I append the intermediate certificate only to the ECDSA certificate, referenced in above Nginx config, ssllabs doesn't throw the error. But, I don't know whether this is the correct way. The docs should also cover the scenario where the ECDSA and RSA certificates are issued by different CAs with differing intermediate certificates.

###########################################################       2.6 from    
    (1.379B 2015/09/25 12:35:41)

      This program is free software. Distribution and 
             modification under GPLv2 permitted. 

       Please file bugs @


 Using "OpenSSL 1.0.2-chacha (1.0.2d-dev)" [~181 ciphers] on
 (built: "Jul  6 18:05:33 2015", platform: "linux-x86_64")

Testing now (2016-07-03 23:28) ---> ( <---

 rDNS (    xxx
 Service detected:       HTTP

--> Testing protocols (via sockets except TLS 1.2 and SPDY/NPN)

 SSLv2      not offered (OK)
 SSLv3      not offered (OK)
 TLS 1      offered
 TLS 1.1    offered
 TLS 1.2    offered (OK)
 SPDY/NPN   not offered

--> Testing ~standard cipher lists

 Null Ciphers                 not offered (OK)
 Anonymous NULL Ciphers       not offered (OK)
 Anonymous DH Ciphers         not offered (OK)
 40 Bit encryption            not offered (OK)
 56 Bit encryption            not offered (OK)
 Export Ciphers (general)     not offered (OK)
 Low (<=64 Bit)               not offered (OK)
 DES Ciphers                  not offered (OK)
 Medium grade encryption      not offered (OK)
 Triple DES Ciphers           not offered (OK)
 High grade encryption        offered (OK)

--> Testing (perfect) forward secrecy, (P)FS -- omitting 3DES, RC4 and Null Encryption here


--> Testing server preferences

 Has server cipher order?     yes (OK)
 Negotiated protocol          TLSv1.2
 Negotiated cipher            ECDHE-ECDSA-AES128-GCM-SHA256, 256 bit ECDH
 Cipher order

--> Testing server defaults (Server Hello)

 TLS server extensions        server name, renegotiation info, EC point formats
 Session Tickets RFC 5077     (none)
 Server key size              EC 256 bit
 Signature Algorithm          SHA256 with RSA
 Fingerprint / Serial         SHA1 xxx / xxx
                              SHA256 xxx
 Common Name (CN)    (works w/o SNI)
 subjectAltName (SAN) 
 Issuer                       Let's Encrypt Authority X3 (Let's Encrypt from US)
 EV cert (experimental)       no 
 Certificate Expiration       >= 60 days (2016-06-28 22:02 --> 2016-09-26 22:02 +0200)
 # of certificates provided   2
 Certificate Revocation List  
 OCSP URI           
 OCSP stapling                not offered
 TLS timestamp                random values, no fingerprinting possible 

--> Testing HTTP header response @ "/"

 HTTP Status Code             200 OK
 HTTP clock skew              -2 sec from localtime
 Strict Transport Security    182 days=15768000 s, just this domain
 Public Key Pinning           # of keys: 32, 182 days=15768000 s, just this domain
                              matching host key: xxx
 Server banner                lol
 Application banner           X-Powered-By: lol
 Cookie(s)                    (none issued at "/")
 Security headers             X-Frame-Options: deny
                              X-XSS-Protection: 1; mode=block
                              X-Content-Type-Options: nosniff
                              Content-Security-Policy: default-src 'self'; font-src 'self' data:; img-src 'self' data: https://*; script-src 'self' 'unsafe-eval' 'unsafe-inline'; style-src 'self' 'unsafe-inline'
 Reverse Proxy banner         --

--> Testing vulnerabilities

 Heartbleed (CVE-2014-0160)                not vulnerable (OK)
 CCS (CVE-2014-0224)                       not vulnerable (OK)
 Secure Renegotiation (CVE-2009-3555)      not vulnerable (OK)
 Secure Client-Initiated Renegotiation     not vulnerable (OK)
 CRIME, TLS (CVE-2012-4929)                not vulnerable (OK)
 BREACH (CVE-2013-3587)                    no HTTP compression (OK) (only "/" tested)
 POODLE, SSL (CVE-2014-3566)               not vulnerable (OK)
 TLS_FALLBACK_SCSV (RFC 7507), experim.    Downgrade attack prevention supported (OK)
 FREAK (CVE-2015-0204)                     not vulnerable (OK)
 LOGJAM (CVE-2015-4000), experimental      not vulnerable (OK), common primes not checked. See below for any DH ciphers + bit size
 BEAST (CVE-2011-3389)                     no CBC ciphers for TLS1 (OK)
 RC4 (CVE-2013-2566, CVE-2015-2808)        no RC4 ciphers detected (OK)

--> Testing all locally available 181 ciphers against the server, ordered by encryption strength

Hexcode  Cipher Suite Name (OpenSSL)    KeyExch.   Encryption Bits        Cipher Suite Name (RFC)
 xc030   ECDHE-RSA-AES256-GCM-SHA384    ECDH 256   AESGCM     256         TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384             
 xc02c   ECDHE-ECDSA-AES256-GCM-SHA384  ECDH 256   AESGCM     256         TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384           
 xc028   ECDHE-RSA-AES256-SHA384        ECDH 256   AES        256         TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384             
 xc024   ECDHE-ECDSA-AES256-SHA384      ECDH 256   AES        256         TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384           
 xc014   ECDHE-RSA-AES256-SHA           ECDH 256   AES        256         TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA                
 xc00a   ECDHE-ECDSA-AES256-SHA         ECDH 256   AES        256         TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA              
 xc02f   ECDHE-RSA-AES128-GCM-SHA256    ECDH 256   AESGCM     128         TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256             
 xc02b   ECDHE-ECDSA-AES128-GCM-SHA256  ECDH 256   AESGCM     128         TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256           
 xc027   ECDHE-RSA-AES128-SHA256        ECDH 256   AES        128         TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256             
 xc023   ECDHE-ECDSA-AES128-SHA256      ECDH 256   AES        128         TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256           
 xc013   ECDHE-RSA-AES128-SHA           ECDH 256   AES        128         TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA                
 xc009   ECDHE-ECDSA-AES128-SHA         ECDH 256   AES        128         TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA              

Done now (2016-07-03 23:29) ---> ( <---
Last edited 2 years ago by david.sardari@… (previous) (diff)

comment:13 Changed 2 years ago by mdounin

LibreSSL doesn't have support for multiple certificate chains and mostly similar to OpenSSL 1.0.1.

Note: See TracTickets for help on using tickets.