Obtain an elliptic curve certificate from Let's Encrypt

You can ask Let’s Encrypt to sign a certificate by sending the Certificate Signing Request (CSR). In Public Key Infrastructure (PKI) systems, a CSR is a message sent from an applicant to a Certificate Authority (CA) in order to apply for a digital identity certificate. It usually contains the Common Name (CN), the country, the e-mail address, the public key for which the certificate should be issued and much more. Concerning the common name it should be the fully qualified domain name (FQDN) of your server. It must match exactly what you type in your web browser or you will receive a name mismatch error.

In this article, we will generate a private key using Elliptic Curve Digital Signature Algorithm (ECDSA) with the NIST P-384 (secp384r1) curve.

Information and requirements

These elements are to be taken into consideration to follow this article:

• the manipulations are carried out on CentOS 8.2.2004,
• throughout this post, we will use domain.fr as example, change it with your own domain name.

Preliminary

Create directories.

sudo mkdir -p /etc/pki/tls/private/domain.fr/
sudo mkdir -p /etc/pki/tls/certs/domain.fr/


Generate the private key

sudo openssl ecparam -check -name secp384r1 -genkey -noout -out /etc/pki/tls/private/domain.fr/privkey.pem -rand /dev/urandom


Set the correction permission

sudo chmod 440 /etc/pki/tls/private/domain.fr/privkey.pem


Flags explanation:

• ecparam: this command is used to manipulate or generate elliptic curve parameter files,
• -check: validate the elliptic curve parameters,
• -name secp384r1: use the elliptic curve parameters with the specified short name,
• -genkey: this option will generate an elliptic curve private key using the specified parameters,
• -noout: this option inhibits the output of the encoded version of the parameters,
• -out /etc/pki/tls/private/domain.fr/privkey.pem: it specifies the output filename,
• -rand /dev/urandom: a file containing random data used to seed the random number generator.

Verify that OpenSSL do things correctly

sudo openssl ec -in /etc/pki/tls/private/domain.fr/privkey.pem -noout -text
Private-Key: (384 bit)
priv:
<hidden>
pub:
<hidden>
ASN1 OID: secp384r1
NIST CURVE: P-384


Flags explanation:

• ec: the command is used to process elliptic curve keys. They can be converted between various forms and their components printed out,
• -in /etc/pki/tls/private/domain.fr/privkey.pem: it specifies the input filename to read a key from,
• -noout: this option prevents output of the encoded version of the key,
• -text: it prints out the public, private key components and parameters.

We can see that the curve is the good one (P-384).

Create the OpenSSL configuration for the certificate

Next, we must create an OpenSSL configuration file with parameters specific to the domain for which we wish to obtain a TLS certificate. Copy the following content into /etc/openssl.cnf and modify it to fit your needs.

[ req ]
prompt = no
encrypt_key = no
default_md = sha512
distinguished_name = dname
req_extensions = reqext

[ dname ]
CN = domain.fr

[ reqext ]
subjectAltName = DNS:domain.fr, DNS:*.domain.fr


The [ req ] section contains:

• prompt = no: tells OpenSSL to get as much configuration as it can from the configuration file,
• encrypt_key = no: tells OpenSSL not to encrypt the private key with a password,
• default_md = sha512: tells OpenSSL to sign the CSR with SHA512,
• distinguished_name = dname: tells OpenSSL to look for a [ dname ] section for Distinguished Name (DN) configuration options,
• req_extensions = reqext: tells OpenSSL to look for a [ reqext ] section for Requested Extensions configuration options.

The [ dname ] section contains:

• CN = domain.fr: specifies the domain name for the certificate,
• emailAddress = contact@domain.fr: the e-mail address to be notified.

The [ reqext ] section contains:

• subjectAltName = DNS:domain.fr, DNS:*.domain.fr: list of Server Alternative Name (SAN) for the certificate.

If you don’t want a wildcard certificate remove , DNS: *.domain.fr in subjectAltName.

Create a CSR

The final client-side step is to generate the CSR, that we will then pass to Let’s Encrypt to sign, and return to us the signed certificate.

sudo openssl req -utf8 -new -key /etc/pki/tls/private/domain.fr/privkey.pem -out /etc/pki/tls/certs/domain.fr/csr.pem -config /etc/openssl.cnf -rand /dev/urandom


Flags explanation:

• req: the command is used to create and process certificate requests in PKCS#10 format,
• -utf8: this option causes field values to be interpreted as UTF8 strings, by default they are interpreted as ASCII,
• -new: this option generates a new certificate request,
• -key /etc/pki/tls/private/domain.fr/privkey.pem: it specifies the file to read the private key from,
• -out /etc/pki/tls/certs/domain.fr/csr.pem: it specifies the output filename to write to,
• -config openssl.conf: it allows an alternative configuration file to be specified,
• -rand /dev/urandom: a file or files containing random data used to seed the random number generator.

Verify that OpenSSL do things correctly

openssl req -in /etc/pki/tls/certs/domain.fr/csr.pem -noout -text -verify
verify OK
Certificate Request:
Data:
Version: 1 (0x0)
Subject: CN = domain.fr, emailAddress = contact@domain.fr
Subject Public Key Info:
Public Key Algorithm: id-ecPublicKey
Public-Key: (384 bit)
pub:
<hidden>
ASN1 OID: secp384r1
NIST CURVE: P-384
Attributes:
Requested Extensions:
X509v3 Subject Alternative Name:
DNS:domain.fr
Signature Algorithm: ecdsa-with-SHA512
<hidden>


Flags explanation:

• -in /etc/pki/tls/certs/domain.fr/csr.pem: it specifies the input filename to read a request from,
• -noout: this option prevents output of the encoded version of the request,
• -text: it prints out the certificate request in text form,
• -verify: it verifies the signature on the request.

Ask Let’s Encrypt to sign the certificate

The last step is to pass the CSR to Let’s Encrypt with an Automatic Certificate Management Environment (ACME) client. Here, we will use certbot. We will consider that the domain is registered with OVH. We can use the corresponding plugin to handle verification, which is extremely convenient, requiring no manual intervention in the process. If you use another DNS provider, you can read the documentation here.

Install required utilities

sudo dnf -y install epel-release
sudo dnf -y install certbot python3-certbot-dns-ovh


Creating API keys

The ACME client must access to the OVH account to perform DNS modifications. To do this, you have to create API keys here. Fill-in the fields as follow:

• Account ID or email address: put here your OVH’s NIC handle,
• Password: OK you know,
• Script name: give it a name,
• Script description: give it a description,
• Validity: I advise you to set this value to Unlimited,
• Rights: GET /domain/zone/*, PUT /domain/zone/*, POST /domain/zone/* and DELETE /domain/zone/*,
• Restricted IPs: you can set an IP address from which queries can be sent to the server.

By clicking on Create keys, if your multi-factor authentication is enabled (I hope to), you’ll receive the OTP code. Once authenticated, 5 fields will appear, 3 are important: Application Key, Application Secret and Consumer Key.

Copy the following content into /etc/.ovh.ini.

dns_ovh_endpoint = ovh-eu
dns_ovh_application_key = <application_key>
dns_ovh_application_secret = <application_secret>
dns_ovh_consumer_key = <consumer_key>


Then, set the correct permission.

sudo chmod 600 /etc/.ovh.ini


Send the request

It is generally advisable to do a --dry-run first to make sure everything is in order.

sudo certbot certonly --dry-run --dns-ovh --dns-ovh-credentials /etc/.ovh.ini --domain "domain.fr" --domain "*.domain.fr" --csr /etc/pki/tls/certs/domain.fr/csr.pem
[...]

IMPORTANT NOTES:
- The dry run was successful.


If you don’t want a wildcard certificate, remove --domain "*.domain.fr". The SANs you specify in subjectAltName must match in text and number with what you specify via the --domain flag.

The flag --csr /etc/pki/tls/certs/domain.fr/csr.pem tells certbot that we already have a certificate that we just need Let’s Encrypt to sign for us.

The certbot client will check that the list of domains requested on the command line matches the domains listed in the certificate.It will use the OVH DNS plugin to verify the ownership of the domain, and let us know if anything is wrong. If nothing is wrong, it will tell you: The dry run was successful like right here.

If everything is good, you can ask for the signature of your certificate for real.

sudo certbot certonly --dns-ovh --dns-ovh-credentials /etc/.ovh.ini --domain "domain.fr" --domain "*.domain.fr" --csr /etc/pki/tls/certs/domain.fr/csr.pem


After a delay, the client will produce 3 files:

• 0000_cert.pem: the signed certificate,
• 0000_chain.pem: the root and intermediate certificates,
• 0001_chain.pem: the certificate plus intermediates.

At this point, the CSR can be deleted.

sudo rm -f /etc/pki/tls/certs/domain.fr/csr.pem


You can inspect the certificates returned by the client with OpenSSL using the x509 command.

openssl x509 -in 0001_chain.pem -noout -text


Flags explanation:

• x509: this command is a multi purpose certificate utility,
• -in 0001_chain.pem: it specifies the input filename to read a certificate from,
• -noout: this option prevents output of the encoded version of the certificate,
• -text: it prints out the certificate in text form.

Move certificates and key

sudo mv 0000_cert.pem /etc/pki/tls/certs/domain.fr/cert.pem
sudo mv 0000_chain.pem /etc/pki/tls/certs/domain.fr/chain.pem
sudo mv 0001_chain.pem /etc/pki/tls/certs/domain.fr/fullchain.pem


Set correction permissions

sudo chmod 444 /etc/pki/tls/certs/domain.fr/*.pem