ECDSA and RSA certificate in parallel with NGINX and Let's Encrypt
With version 1.11.0 of NGINX it is now possible to serve content via https
using RSA
and ECDSA
certificates in parallel.
ECDSA
is another approach to cryptographically sign messages and comes with some advantages compared to RSA
. According to a comparison made by the BSI an ECDSA
key with a key length of 256 bit provides about the same security as a RSA
key of 2048 to 3072 bit (what is about 128 bit symmetric key size). The smaller key length of the ECDSA
key results in less computing power needed for generating message signatures during TLS connections. This advantage becomes measurable on systems with thousands of concurrent TLS sessions.
Happily Let's Encrypt issues certificates for ECDSA keys since 10th of February so that it is possible to get a free ECDSA
certificate accepted by all major browsers. Unfortunately these certificates are still signed by the Let's Encrypt Authority X3 intermediate certificate, which is a RSA
certificate. The ability to get the end-entity certificates signed with an ECDSA
intermediate certificate by Let's Encrypt is scheduled for this year. But since the certificate chain is validated on the client, this does not have an impact on the servers TLS performance (except the larger intermediate certificate sent to the client).
An ECDSA
key and CSR can be generated similarly to its RSA
equivalent using OpenSSL.
user@host:~$ openssl ecparam -name <curve> -genkey -noout -out service.ecdsa.key user@host:~$ openssl req -new -sha256 -key service.ecdsa.key -subj "/CN=www.example.com" -out service.ecdsa.csr
The <curve>
parameter specifies the elliptic curve used for the key. openssl ecparam -list_curves
will print a list with all curves built into OpenSSL.
According to the Let's Encrypt forum and some tests I ran against the Let's Encrypt staging server, only certificates for keys using the curves prime256v1
and secp384r1
are issued. secp521r1
seemed to be in discussion for some time but is not enabled at the moment. This is not a very satisfying situation, since the supported curves seem to have some flaws and are not assumed to be totally secure. Hopefully more secure curves will be supported by Let's Encrypt soon.
The generated CSR can be submitted to Let's Encrypt the same way as done when using RSA
keys. Here is an example using acme-tiny.
user@host:~$ acme_tiny.py --account-key letsencrypt.key --csr service.ecdsa.csr --acme-dir acme-challenge/ > service.ecdsa.crt
If the domain validation was successful, this will leave you with a signed ECDSA
certificate in service.ecdsa.crt
.
user@host:~$ openssl x509 -noout -in service.ecdsa.crt -text ... Subject Public Key Info: Public Key Algorithm: id-ecPublicKey Public-Key: (384 bit) pub: 04:43:90:a2:b2:79:55:df:ba:e8:fc:a9:ef:04:d0: 12:d6:f7:6b:56:78:58:b1:db:3a:05:c3:bc:62:4d: 14:c6:c2:c7:ce:1c:11:7a:1c:a7:15:bf:00:96:69: 0c:3c:17:e8:3b:84:48:f5:85:d1:d3:12:b5:01:af: d9:89:29:80:f4:45:27:d8:98:b4:20:15:d4:e0:bb: 89:6e:d6:08:44:71:37:44:1c:8f:a5:7d:90:f6:b8: 7e:68:e5:b5:41:29:24 ASN1 OID: secp384r1 ...
To add multiple certificates to your NGINX configuration, the ssl_certificate
and ssl_certificate_key
directives can be specified multiple times.
- /etc/nginx/conf.d/www000.conf
server { listen 0.0.0.0:443; listen [::]:443; server_name www.example.com; ... # RSA ssl_certificate /etc/nginx/tls/service.rsa.crt; ssl_certificate_key /etc/nginx/tls/service.rsa.key; # ECDSA ssl_certificate /etc/nginx/tls/service.ecdsa.crt; ssl_certificate_key /etc/nginx/tls/service.ecdsa.key; ... }
Make sure that the Let’s Encrypt Authority X3 intermediate certificate is appended in one of the files referenced by the ssl_certificate
directive. If the intermediate certificate is present in both files, this will cause NGINX to send it twice what might result in errors on the client site.
Restart NGINX and check if your can connect with ECDSA
ciphers enabled.
user@worksation:~$ openssl s_client -connect www.example.com:443 -status -tlsextdebug -tls1_2 -cipher ECDHE-ECDSA-AES128-SHA256 </dev/null ... --- Certificate chain 0 s:/CN=www.example.com i:/C=US/O=Let's Encrypt/CN=Let's Encrypt Authority X3 1 s:/C=US/O=Let's Encrypt/CN=Let's Encrypt Authority X3 i:/O=Digital Signature Trust Co./CN=DST Root CA X3 --- ... Server public key is 384 bit ... Cipher : ECDHE-ECDSA-AES128-SHA256 ...
Try the same with a RSA
cipher
user@worksation:~$ openssl s_client -connect www.example.com:443 -status -tlsextdebug -tls1_2 -cipher ECDHE-RSA-AES128-SHA256 </dev/null ... --- Certificate chain 0 s:/CN=www.example.com i:/C=US/O=Let's Encrypt/CN=Let's Encrypt Authority X3 1 s:/C=US/O=Let's Encrypt/CN=Let's Encrypt Authority X3 i:/O=Digital Signature Trust Co./CN=DST Root CA X3 --- ... Server public key is 4096 bit ... Cipher : ECDHE-RSA-AES128-SHA256 ...
Which of the both certificates is actually used depends on the order of the ciphers listed within the ssl_ciphers
directive and the capabilities of the connecting client. For example the cipher recommendations given in the Mozilla wiki prefer ECDSA
ciphers over their RSA
counterparts.
After configuration, go ahead and test your whole TLS configuration with public tools like Qualys SSL test or testssl.sh. How to improve your TLS configuration for NGINX is also documented in my article „Secure webspaces with NGINX, PHP-FPM chroots and Let's Encrypt“.