Let's Encrypt recently started signing certificates that use ECDSA keys so I figured I'd grab one and give it a try. ECDSA offers considerable increases in both security and performance compared to RSA and boy can you see it!


I'm not going to do a deep dive on ECDSA but the headline facts are that it's faster and more secure than RSA, the only downfall right now is how widespread support is. That's partly why I'm doing this testing and I've setup a subdomain to use my ECDSA certificate. You can visit the subdomain and if the page loads then your browser definitely supports ECDSA!


ecdsa in chrome

How much faster?

To see just how much faster ECDSA is I did a little testing. I wanted to use the openssl s_time tool but I had no luck with that at all on Ubuntu.

openssl s_time -connect scotthelme.co.uk:443 -new

That should have given me information on the timing of the connections but failed horribly for me.

No CIPHER specified
Collecting connection statistics for 30 seconds
139923721516704:error:14094410:SSL routines:SSL3_READ_BYTES:sslv3 alert handshake failure:s3_pkt.c:1262:SSL alert number 40

No amount of tinkering got it sorted so I resorted to using the time command instead.

time openssl s_client -connect rsa2048.scotthelme.co.uk:443 -cipher ECDHE-RSA-AES128-GCM-SHA256 < /dev/null

I have another subdomain here called https://rsa2048.scotthelme.co.uk that I'm using because on my 'live' site I'm using a 4096bit RSA key, which isn't the norm. The subdomain has, as you probably guessed, a 2048bit RSA key for testing purposes. The command dumps out the standard s_client output but then has timing information at the bottom too.

real    0m0.636s
user    0m0.016s
sys     0m0.006s

So we can see the connection is taking 636ms in this particular instance and if you run the command a few times over it only deviates a few ms either way for me, it's pretty steady. To compare this to the ECDSA certificate all I need to do is change the subdomain and the cipher in use. I'm specifying the cipher so I can control it and ensure a fair test.

time openssl s_client -connect ecdsa.scotthelme.co.uk:443 -cipher ECDHE-ECDSA-AES128-GCM-SHA256 < /dev/null

So if we run this it now gives us a direct comparison between the RSA and ECDSA certificate.

real    0m0.377s
user    0m0.009s
sys     0m0.005s

So that's pretty conclusive! The handshake is almost 100% faster when using the ECDSA certificate! Again, you can run this command a few times to get a baseline but that's an incredible boost in performance by just switching out the certificate you use. The other really awesome thing here is that the ECDSA key is only 256bit compared to the RSA key which is 2048bit, but, the ECDSA key offers more security. At only 256bit the ECDSA key is almost as strong as a 3072bit RSA key, a considerable step up in security with your 50% reduction in overhead!

Generating the ECDSA key

If you want to generate an ECDSA key to get a certificate from Let's Encrypt then you can use the following commands. Remove the -aes128 from the end of the command if you don't want to set a password on the key.

openssl ecparam -genkey -name secp256r1 | openssl ec -out ecdsa.key -aes128

That should give you some output:

read EC key
using curve name prime256v1 instead of secp256r1
writing EC key
Enter PEM pass phrase:
Verifying - Enter PEM pass phrase:

You have set a password on the key but if you want to remove it, use this.

openssl ec -in ecdsa.key -out ecdsa.key
read EC key
Enter PEM pass phrase:
writing EC key

That will read in the key and write it back out without the password. You can single command it as it turns out, thanks to @jamesspi for the tip.

openssl ecparam -genkey -name secp256r1 > ecdsa.key

That will just generate the key without the password and the need to remove it which is great if you're automating things somewhere. I've left my method above though as some other guides detail how to generate the key but not remove the password. Carrying on, if you're like me and use HPKP, you will need to create a pin for the new key.

openssl ec -in ecdsa.key -pubout | openssl pkey -pubin -outform der | openssl dgst -sha256 -binary | openssl base64

That gives me the pin I need to set in my header.


The next step is to generate the CSR.

openssl req -new -sha256 -key ecdsa.key -out ecdsa.scotthelme.csr -config openssl.cnf

Then the final step is to pass that in to acme_tiny to get a signed certificate! You can read my blog on Getting started with Let's Encrypt for the full guide to setup acme_tiny and Let's Encrypt.

python acme_tiny.py --account-key account.key --csr ecdsa.scotthelme.csr --acme-dir challenges/ > signed.crt
Parsing account key...
Parsing CSR...
Registering account...
Already registered!
Verifying ecdsa.scotthelme.co.uk...
ecdsa.scotthelme.co.uk verified!
Signing certificate...
Certificate signed!

And there we have it, that's all there is to it. All said and done nothing has really changed in terms of what we need to do and maintenance going forwards is exactly the same. I'm still looking into the browser support aspect but if it is widespread enough then switching to ECDSA is an absolute no brainer.