openssl
.RSA is a most popular public-key cryptography algorithm. Certificates with RSA keys are the gold standard and the present of the current Internet PKI security. It’s old and battle tested technology, and that’s highly important from the security perspective. Elliptic curve cryptography is an alternative approach to public-key cryptography over the current RSA standard.
RSA algorithm can be used for encryption and digital signing, while ECC can only be used for signing.
The security of a key depends on its size and its algorithm. Some algorithms are easier to break than others. Breaking an RSA key requires to factor the product of two large numbers. Breaking an ECC key requires to find the discrete logarithm between points on an elliptic curve, and there is no progress so far to achieve this.
Algorithms require different key size for the same level of security. ECC can use smaller key sizes. Here is table with key size comparison:
+----------------------+-----------------+--------------------+| Symmetric Key length | RSA key length | ECC key length |+----------------------+---------- ------+--------------------+| 80 | 1024 | 160 || 112 | 2048 | 224 || 128 | 3072 | 256 || 192 | 7680 | 384 || 256 | 15360 | 512 |+----------------------+-----------------+--------------------+
For example, 256-bit ECC key is equivalent to RSA 3072-bit key, providing 128 bits of security:
Smaller keys are less computationally intensive for generating signatures because the math involves smaller numbers. However, while ECC is faster at signature generation, it’s slower than RSA at signature verification. Let’s measure this using openssl as a benchmarking tool:
$ openssl speed ecdsap256 rsa2048
sign/s verify/s
rsa 2048 bits 679.0 23489.0256 bit ecdsa (nistp256) 15581.9 6211.7
Smaller ECC public key means smaller certificate size — less data to pass around, quicker to download, and faster TLS handshake.
If you want more security, RSA does not scale well — you have to increase the RSA modulus size far faster than the ECDSA curve size. 1024 bit RSA keys are obsolete, 2048 are the current standard size. If you need to go farther, you’d stuck. First, if CA does not provide 4096 bit RSA keychain, signing your own 4096 bit RSA key with a 2048 RSA intermediary doesn’t make sense. Second, note that every doubling of an RSA private key degrades TLS handshake performance approximately by 6–7 times. So, if you need more security, choose ECC.
While ECC has some benefits, there are also some drawbacks. The technology is not that mature and tested as RSA. And there is a compatibility question, however seems it can be used on most operating systems and modern browsers. Check out these resources for ECC compatibility details.
There a lot of elliptic curves out there. Most popular and supported by major browsers are P-256, P-384, P-521, x25519. While P-256 and P-384 are part of NIST’s Suite B algorithms, P-521 and x25519 are not. Google Chrome has dropped support for the P-521 curve, same is going on regarding NSS/Firefox. To maximise interoperability with existing browsers and servers, stick to P-256 prime256v1
and P-384 secp384r1
curves.
To view list of all available ECC curves, that OpenSSL library supports:
openssl ecparam -list_curves
To view supported curves of your browser, use SSL Labs Client Test.
To conclude, ECDSA certificates pros and cons:
You don’t need to choose between RSA and ECC exclusively. You can setup hybrid configuration, serving ECDSA certificate first, with a fallback to RSA certificate for non-supporting clients.
Usually, before you send a request to a CA to issue a certificate, you need to generate private key and CSR (certificate signing request). It’s super easy with openssl
tool.
Generate RSA key at a given length:
openssl genrsa -out example.key 2048
Generate EC key with a given curve:
openssl ecparam -genkey -name secp384r1 | openssl ec -out ec.key
Generate a CSR from existing private key with a given subject info:
openssl req -new -key example.key -out example.csr -subj "/CN=example.com" -sha256
Or you can combine both key and CSR creation within a single command:
openssl req -nodes -newkey rsa:2048 -keyout example.key -out example.csr -sha256
If you want to experiment with ECDSA and RSA certificates, the best option is to use LetsEncrypt Certificate Authority, which allows to generate free domain validated certificates in automated fashion.
In order to obtain a certificate, you need to prove the ownership of the domain.
Usually this process requires manual work: generate a private key and a CSR (Certificate Signing Request) with relevant subject info and common name, send a CSR to the CA, and finally prove the domain’s ownership using a selected challenge method:
LetsEncrypt automates this process by using a client that can talk ACME protocol (Automatic Certificate Management Environment). Client typically runs on your web host, and communicates to LetsEncrypt CA or another ACME-compatible server. Client receives unique token from the server, generates key from it, starts a standalone web server listening on port 80 and serves key at special URI, like http://example.com/.well-known/acme-challenge. In case of DNS challenge method, client can automatically add CNAME record to your DNS configuration depending on DNS server/provider. LetsEncrypt CA then makes HTTP or DNS request to your domain to retrieve the key derived from the token. Successful response proves the domain ownership, and CA issues the requested certificate.
You don’t generate private key and CSR on your own, this is handled by the client software on your web host. Note, that private keys are never generated/leaked on CA servers, and stored on your web host exclusively. Clients typically provide some way to adjust some settings through client, like key length, subject alternative names, or if you need further customisation, you can fallback to providing custom CSR for more flexibility and control.
Note, that Lets Encrypt’s certificates are only valid for 90 days. This is to encourage users to automate certificate issuance and renewal process.
Be aware, that LetsEncrypt CA production servers put strict rate limits:
While you’re trying and experimenting, most likely you will hit the latter one, so it’s better to use LetsEncrypt staging environment with much relaxed limits.
The staging environment intermediate certificate (“Fake LE Intermediate X1”) is issued by a root certificate not present in browser/client trust stores, so you might want to add “Fake LE Root X1” as trusted one while testing.
There are variety of ACME clients: certbot, acme.sh, lego, and others. I’m not going to dive into client specifics here — anyway docs would explain better.
Personally, I’ve found that easier and faster way to get things done is to issue certs through some Docker image. This is for lazy people, and those, who don’t want to spend much time digging with LetsEncrypt and parsing docs.
I’ve prepare one: asamoshkin/letsencrypt-certgen on Docker Hub and Github link. This Docker image provides a simple single entrypoint to obtain and manage SSL certificates from LetsEncrypt CA. It encapsulates two popular ACME clients: certbot and acme.sh, which are used to obtain RSA and/or ECDSA certificates respectively. We need both, because certbot is not capable of issuing ECDSA certificates (to be more correct, only thru custom CSR, but then you lose the ability to renew, revoke and further manage such certificate).
Here is an example of issuing both ECDSA (prime256v1 curve) and RSA (2048) certificates for the single domain foobbz.site
docker run \ -v /var/ssl:/var/ssl \ -p 80:80 \ -e DOMAINS=foobbz.site \ --rm \ asamoshkin/letsencrypt-certgen issue
The requirement is to run this image on the server, configured for the domain you want to get certificates for (where your DNS A record points to). Server’s port 80 should be open by a firewall, so LetsEncrypt CA server can perform validation challenge.
Once done, certificates, keys and related files are stored at /var/ssl/$domain_common_name
path, on a/var/ssl
volume you’ve mounted into the container before.
# tree /var/ssl/var/ssl└── foobbz.site ├── certs │ ├── cert.ecc.pem │ ├── cert.rsa.pem │ ├── chain.ecc.pem │ ├── chain.rsa.pem │ ├── fullchain.ecc.pem │ └── fullchain.rsa.pem └── private ├── privkey.ecc.pem └── privkey.rsa.pem
All files are encoded in PEM format:
cert.rsa.pem
, cert.ecc.pem
- generated certificates (RSA or ECDSA)chain.[type].pem
- chain of intermediate CA certificates (e.g. Fake LE Intermediate X1)fullchain.[type].pem
- certificate bundled with any intermediate CA certificates. This is suitable for Nginx directive ssl_certificate
, which requires a bundle, instead of leaf certificate.privkey.[type].pem
- private key fileYou’re not limited to a single certificate for a single domain. You can issue several certificates for several domains, or single certificate covering multiple domains using SAN (X.509 subject alternative names extension). You can opt to generate ECDSA or RSA certificate only with custom key length or elliptic curve.
First, prepare a file with domains lists. Each line represents individual certificate to be issued. First name within each line is a common name, whereas subsequent comma-separated names are certificate alternative names (SAN).
# cat /root/domains.txtfoobbz.site,www.foobbz.site,web.foobbz.sitefoobbz2.site,www.foobbz.site
Run a command to issue two ECDSA “secp384r1” certs only (no RSA cert) for foobbz.site
and foobbz2.site
domains:
docker run \ -v /var/ssl:/var/ssl \ -v /root/domains.txt:/etc/domains.txt \ -p 80:80 \ -e RSA_ENABLED=0 \ -e ECDSA_KEY_LENGTH=ec-384 \ -e DOMAINS=/etc/domains.txt \ --rm \ asamoshkin/letsencrypt-certgen issue
The main use case for the image is to trigger a one-shot command to issue certificates, but you can also further manage your certificates by renewing, revoking or deleting them using same image.
You can use this image ad-hoc at a build time, at a run-time prior to Nginx/Apache startup, or by running it from a cron job to renew certificates on regular basis. The idea is that LetsEncrypt stuff is encapsulated within a single container , and you don’t need to pollute your Nginx/Apache container.
More complete list of features:
For further details and examples, check out samoshkin/docker-letsencrypt-certgen on Github. You can also just check out the source code to learn how to consume certbot and acme.sh clients.
Also, there is a ZeroSSL docker image worth checking out.
Once you’ve generated certificates, it’s time to configure your web server. Nginx allows hybrid side by side RSA and ECDSA certificates, and will serve one or another during TLS handshake depending on agreed cipher suite.
server {listen 443 ssl default_server_;_ server_name foobbz.site www.foobbz.site_;_
# RSA certificates ssl_certificate /var/ssl/foobbz.site/certs/fullchain.rsa.pem_;_ssl_certificate_key /var/ssl/foobbz.site/private/privkey.rsa.pem_;_
# ECDSA certificates ssl_certificate /var/ssl/foobbz.site/certs/fullchain.ecc.pem_;_ssl_certificate_key /var/ssl/foobbz.site/private/privkey.ecc.pem_;
}_
Also you need to tell Nginx to prefer ECDSA over RSA for authentication by correctly ordering your cipher suites (notice how suites with “ECDSA” go first, followed by suites with “aRSA”):
ssl_prefer_server_ciphers on;ssl_ciphers "EECDH+ECDSA+AESGCM:EECDH+aRSA+AESGCM:EECDH+ECDSA+SHA384:EECDH+ECDSA+SHA256:EECDH+aRSA+SHA384:EECDH+aRSA+SHA256:EECDH:DHE+AESGCM:DHE:!RSA!aNULL:!eNULL:!LOW:!RC4:!3DES:!MD5:!EXP:!PSK:!SRP:!DSS:!CAMELLIA:!SEED";
That’s it, now start your Nginx, and let’s test our configuration using openssl as a TLS client and extract certificate. Or you can use Qualys SSL Labs Server Test to view certificates.
$ openssl s_client -host foobbz.site -port 443 -cipher ECDHE-ECDSA-AES128-GCM-SHA256 2>&1 < /dev/null | sed -n '/-----BEGIN/,/-----END/p' | openssl x509 -noout -text
Looking at a public key, we can ensure that ECDSA certificate is served.
Subject Public Key Info:Public Key Algorithm: id-ecPublicKeyPublic-Key: (256 bit)pub:04:47:ee:86:9f:c9:5a:81:16:89:38:f7:5b:9d:ba:6b:8b:d2:aa:8e:1e:54:67:f7:f1:44:84:5f:10:df:7f:df:16:f4:2f:d6:c1:78:b8:71:68:e9:ee:78:82:fc:2e:ae:96:e9:a3:b7:26:c0:ed:41:39:2a:48:f9:0f:28:10:4e:15ASN1 OID: prime256v1NIST CURVE: P-256
Note, that leaf ECDSA certificates are still signed by LetsEncrypt’s RSA certificate chain (Let’s Encrypt Authority X3, DST Root CA X3). LetsEncrypt does not use dedicated EC certificates to sign to build complete EC chain.
0 s:/CN=foobbz.sitei:/C=US/O=Let’s Encrypt/CN=Let’s Encrypt Authority X31 s:/C=US/O=Let’s Encrypt/CN=Let’s Encrypt Authority X3i:/O=Digital Signature Trust Co./CN=DST Root CA X3
Now, let’s use another cipher suite (ECDHE-RSA-AES128-GCM-SHA256) to test RSA configuration.
openssl s_client -host foobbz.site -port 443 -cipher ECDHE-RSA-AES128-GCM-SHA256 2>&1 < /dev/null | sed -n '/-----BEGIN/,/-----END/p' | openssl x509 -noout -text
We get 2048 bits RSA public key. By the way, notice the difference in length between them.
Subject Public Key Info:Public Key Algorithm: rsaEncryptionPublic-Key: (2048 bit)Modulus:00:ea:cb:5e:22:dd:93:fe:63:21:c4:bd:b9:07:78:fc:ef:66:38:7a:19:bf:68:58:39:16:86:ee:f6:2c:18:68:9c:32:c0:5b:a4:76:e8:e0:40:0e:6d:29:7c:bc:04:67:b4:1b:05:e8:72:53:24:dc:4f:a6:3d:48:41:2e:83:99:fa:13:20:88:b8:e5:5d:34:57:01:c6:eb:fc:c1:67:e8:e4:ec:58:2c:a2:ce:51:ea:99:c0:bb🇩🇪61:8a:40:76:80:50:48:25:c6:7f:0e:a4:a6:61:e7:25:67:b1:74:ee:1f:f1:75:e8:76:a0:c5:5d:9c:40:48:8b:d3:95:e1:27:d2:d6:ca:14:e4:39:ac:4d:0d:35:23:89:db:4b:ef:60:84:0b:4d:15:76:0e:3c:f5:52:1c:20:ce:d8:03:25:22:7a:37:84:fb:d2:1b:00:ff:31:69:55:65:7d:42:d1:31:99:0c:d6:29:41:36:06:bf:0d🆎31:1a:e6:b0:6a:76:67:2c:7b:c0:5b:34:55:49:e2:4c:d9:e4:40:99:1c:c1:1d:6a:88:c1:53:af:ee🆎b5:2e:e6:76:ff:1c:33:e2:ca:7e:d7:93:e6:23:df💿78:a6:39:f4:04:a2:44:d0:a6:cc:f1:51:2f:5d:dc:5e:ea:ff:57:d7:f1:82:d4:48:11Exponent: 65537 (0x10001)
So, that’s it. Thank you for reading this post.
Reddit user wuunderbar pointed out that Docker container does not have enough entropy to generate key material. One solution I’m aware of is mounting /dev/urandom
from host machine into /dev/random
of the container. See this StackOverflow answer.
docker run -v /dev/urandom:/dev/random ...
asamoshkin/letsencrypt-certgen — Docker Hub — https://hub.docker.com/r/asamoshkin/letsencrypt-certgen/
samoshkin/docker-letsencrypt-certgen: Generate, renew, revoke RSA and/or ECDSA SSL certificates from LetsEncrypt CA using certbot and acme.sh clients in automated fashion — https://github.com/samoshkin/docker-letsencrypt-certgen
Elliptic Curve Cryptography (ECC Certificates) | DigiCert.com — https://www.digicert.com/ecc.htm
ECC — https://support.globalsign.com/customer/portal/articles/1994347-ecc
ECC Compatibility — https://support.globalsign.com/customer/portal/articles/1995283-ecc-compatibility
ECDSA: The digital signature algorithm of a better internet — https://blog.cloudflare.com/ecdsa-the-digital-signature-algorithm-of-a-better-internet/
Let’s Encrypt — Free SSL/TLS Certificates — https://letsencrypt.org/
Certbot — https://certbot.eff.org/
ZeroSSL: Free SSL — https://zerossl.com/
Neilpang/acme.sh: A pure Unix shell script implementing ACME client protocol — https://github.com/Neilpang/acme.sh
RSA and ECDSA performance | securitypitfalls — https://securitypitfalls.wordpress.com/2014/10/06/rsa-and-ecdsa-performance/
Testing out ECDSA certificates — https://scotthelme.co.uk/ecdsa-certificates/