Previously I described a relatively modern set of TLS settings that would give an A+ score on SSLtest. This was based purely on an RSA certificate.
There exist another type of certificate, based on Elliptical Curve cryptography. You may see this referenced as ECC or, for web sites, ECDSA. An ECDSA certificate is smaller than an RSA cert (eg a 256bit ECDSA cert is roughly the equivalent of a 3072bit RSA one). This makes it quicker to send, although at present RSA signatures are quicker to verify than ECDSA.
My setup
I use LetsEncrypt for my certificates, although
(for complicated reasons) I don’t use their standard certbot
client.
Instead I use Dehydrated.
I like this client because it just puts the certs into a directory and
then you can do whatever you like with them (in my case I use ansible
to distribute to lots of different endpoints).
The Dehydrated client also supports ECDSA certificates (indeed, in the
current version it defaults to secp384r1
to generate 384bit certs).
Configuring Dehydrated for both cert types
I want to use both RSA and ECDSA certificates on this site but Dehydrated can only handle one type at a time. Fortunately it has a work around; you can create a second certificate directory and have that override the cert type.
So in my case I configured the app to default to rsa
and then created
an override.
% cat domains.txt
*.sweharris.org sweharris.org > sweharris
*.sweharris.org sweharris.org > sweharris_ecdsa
% grep KEY_ALGO config
KEY_ALGO=rsa
% cat certs/sweharris_ecdsa/config
KEY_ALGO="secp384r1"
With this setup when I run the “refresh” command (dehydrated -c
) it
will place the RSA cert in the sweharris
directory and ECDSA cert in
the sweharris_ecdsa
directory:
% openssl x509 -noout -text -in certs/sweharris/cert.pem | grep Public.Key.Algo
Public Key Algorithm: rsaEncryption
% openssl x509 -noout -text -in certs/sweharris_ecdsa/cert.pem | grep Public.Key.Algo
Public Key Algorithm: id-ecPublicKey
Inside the certs
directory we will find four files of interest. These
are always symbolic links to the latest version, so that provides a
consistent name for us to use:
- cert.pem
This is the public key for the server certificate - privkey.pem
This is the private key for the server. - chain.pem
This file contains the “chaining” certificates needed to generate a chain of trust to the root. - fullchain.pem
This file contains the chaining certificates and the server public key. Sometimes this can be easier to use.
We’ll see, next, how these files are used.
Configuring Apache
I have two primary server types in my environment. They’re either based on CentOS 7 or CentOS 8. The configuration is slightly different.
The certificates
For CentOS 8 it’s pretty simple, we can use the full chain file:
SSLCertificateFile /etc/httpd/conf/sweharris/fullchain.pem
SSLCertificateKeyFile /etc/httpd/conf/sweharris/privkey.pem
SSLCertificateFile /etc/httpd/conf/sweharris_ecdsa/fullchain.pem
SSLCertificateKeyFile /etc/httpd/conf/sweharris_ecdsa/privkey.pem
Now if you try that on CentOS 7 it seems to work, but SSLtest complains about missing chain certificates and small DH key sizes. Not sure why that happens. But fortunately we can use a slightly different path
SSLCertificateFile /etc/httpd/conf/sweharris/cert.pem
SSLCertificateKeyFile /etc/httpd/conf/sweharris/privkey.pem
SSLCertificateFile /etc/httpd/conf/sweharris_ecdsa/cert.pem
SSLCertificateKeyFile /etc/httpd/conf/sweharris_ecdsa/privkey.pem
SSLCertificateChainFile /etc/httpd/conf/sweharris/chain.pem
This works because both certs are signed by LetsEncrypt via the same chain (same intermediate, same root). If that wasn’t the case then we’d need to work a little harder to ensure the chain file had everything that was necessary.
The ciphers
The previous cipher list I provided only selected RSA options; after all, why bother with ECDSA options if you don’t have the right certificate? But now we do. As with the RSA certificates there’s a small number that can be considered “strong”. Indeed, we’ll stick with two
ECDHE-ECDSA-AES256-GCM-SHA384
ECDHE-ECDSA-AES128-GCM-SHA256
There are others which maybe considered (e.g. CHACHA-POLY based), but I don’t believe the oldish versions of openssl on CentOS currently support them.
This results in a cipher suite configuration of:
SSLCipherSuite ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256
You may notice that i no longer have DHE-RSA-AES256-GCM-SHA384
listed as
a cipher, but I still have a couple of CBC based ones there.
The CBC ciphers are still needed because, as we previously saw, older versions of Safari need them, and these old versions don’t work with ECDSA
Safari 6 / iOS 6.0.1
Safari 7 / iOS 7.1
Safari 7 / OS X 10.9
Safari 8 / iOS 8.4
Safari 8 / OS X 10.10
The result
The good news is that we still get an A+ rating! The report also shows the two options for the certificates.
It correctly reports the 6 ciphers as being available
TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 (0xc02c) ECDH secp256r1 (eq. 3072 bits RSA) FS 256
TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (0xc030) ECDH secp256r1 (eq. 3072 bits RSA) FS 256
TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 (0xc02b) ECDH secp256r1 (eq. 3072 bits RSA) FS 128
TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (0xc02f) ECDH secp256r1 (eq. 3072 bits RSA) FS 128
TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384 (0xc028) ECDH secp256r1 (eq. 3072 bits RSA) FS WEAK 256
TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256 (0xc027) ECDH secp256r1 (eq. 3072 bits RSA) FS WEAK 128
Of course the two CBC ciphers are still flagged as weak, but we’re aware of this and they’re needed for Safari compatibility.
Digging further into the report I noticed that most of the clients negotiated ECDSA mode. Only a handful did RSA mode!
Chrome 49 / XP SP3 TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
Safari 6 / iOS 6.0.1 TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384
Safari 7 / iOS 7.1 TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384
Safari 7 / OS X 10.9 TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384
Safari 8 / iOS 8.4 TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384
Safari 8 / OS X 10.10 TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384
Literally everything else that SSLtest emulates that can work with with TLS1.2 all negotiated ECDSA. Interestingly “IE 11 / Win Phone 8.1” previously needed a CBC cipher but it can do ECDSA in GCM mode!
Complication
Although this doesn’t impact me, in an enterprise environment the support for multiple certificate types may be impacted by middleware boxes such as load balancers that do TLS termination (so they can inspect the traffic and do sticky sessions or similar). It’s not much point in doing ECDSA if other devices can’t support it! Similarly you may need the DHE based ciphers to handle limitations of those devices. But the list provided here should be a good starting point.
Conclusion
This didn’t take too long to figure out, once I stopped fighting the
CentOS 7 build and attempting to use the fullchain.pem
file there.
It’s now possible, even for the relatively old CentOS 7, to support modern ciphers and modern public key cryptography. Every cipher used supports forward secrecy and large key sizes. We just need to be careful about any future CBC Oracle attacks!
Of course ECDSA isn’t resilient against quantum computing attacks, but I really don’t think that’s going to be a practical concern for a long time.
Note the full report includes a
secondary domain (*.spuddy.org
) so doesn’t quite match what I’ve
written. I didn’t include a description of that because it’s unnecessary
complication. In this case a client without SNI (are there any that
also support TLS1.2?) would get a cert for a different virtual host and
so get an error. That’s also not relevant to this post :-)