vennedey.net

ECDSA and RSA certificate in parallel with NGINX and Let's Encrypt

On Fri, 03 Jun 2016 18:19:19 +0200 by Falco Nordmann

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“.

Comments

Write a comment
* optional