Using RSA and ECDSA on Apache with CentOS / RedHat

With LetsEncrypt certs

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 :-)