Securing your connections using Elliptic Curve based certs | OpenSSL
Today, a lot of websites have transitioned to elliptic curve based certificates to secure their connections over the Internet. Elliptic curve algorithms use smaller keys for encryption, less computing power and less time to sign.
If you have a non-production website that you want to secure over TLS, this article will guide you step by step on how to generate the private keys and certificates needed.
But before that, let’s have a quick look at what a certificate chain is and how it works.
Understanding Certificate Chain
Let us understand this using an example. Imagine you have received a mail from Alice informing you that he will be your new manager at your new company. It also informs that any further mail received from Alice and signed by him will be a considered valid form of communication.
There is a section in the email with instructions on how to verify the signature of Alice’s mails. This section is like a certificate that gives you confidence in Alice’s future mails.
Sounds cool, but how do we know that this person Alice is not just any random person? After all, this is the first mail you ever received from him, and he claims to be a manager now.

In context of server-client communication, this is similar to a server sending you a certificate that the client would be using to validate the messages it receives from the server. If the certificate could be transmitted to the client (say, your browser) unhampered, it won’t be an issue. But the client has no way of knowing if the certificate is actually coming from the server.
You notice the mail you received from Alice is signed by Bob, who is from the senior leadership team at your new company. But, it is a new place for you, and you don’t know everyone. You obviously haven’t heard of Bob ever before.
Thankfully, there is an attached document on how to verify Bob’s signature and this is signed by one of the co-founders of the company. Before your day 1 at this company, you had a long meeting with the co-founders, at the end of which they handed you a similar document on how to verify their signatures.

You verified the sign of the cofounder, and now you can trust the instructions for verifying Bob’s signature. Henceforth, you also verified Bob’s signature, and now you can trust the mail received from Alice. Alice is indeed your new manager. The cofounder’s certificate is called the root certificate.
The reason why this chain of trust worked is because there was an instruction that you chose to trust — the instruction to verify the cofounders’ signature.
Similarly, for your browser to be able to trust the certificate it receives from the server (which it is not sure of), the certificate is signed by another party. Your browser might simply trust this party (have the party’s certificate in their trust store), or this certificate might be signed by another party. This chain will continue until it terminates to a party that we trust.
To view certificates used by any website in Chrome —
> Click on the lock icon at the left of the page URL
> Click ‘Connection is secure’
> Click ‘Certificate is valid’
> Go to ‘Details’ tab.
Here you can view the contents of the certificate (Certificate Fields) and the certificate chain (Certificate Hierarchy).
Fun Fact: Another key aspect of why this trust-based system in the example given works is that only the person himself can know how to do their own signature. Anyone with the instruction can verify the sign, but no one can replicate the sign. This asymmetric nature of the signatures is crucial. If this is broken (if someone can replicate the signature of the other person), the security of the system is totally compromised.
The same is true for keys and certificates used to authenticate the server-client communication.
Generating Certificates
Let’s now look at the steps to generate the certificate in Linux. We will first generate the root certificate (and private key), and then use this to sign certificates that will be used by your server/website.
1. Generate the CA (Root) Certificate
Certificate is what is used to verify a signature. To create that you also need to create the tool you will use to sign something. This is called the private key. A certificate contains what we call the public key. We will be creating the private key first.
It is better to keep things organised, so we will create two directories first.
# Create two directories for all certs and root private key that you will generate
mkdir certs private
Generate the elliptic curve private key
# Generating ca-key.pem file within private dir
openssl ecparam -out private/ca-key.pem -name prime256v1 -genkey
The certificate, alongside the public key that will be generated from this private key, holds information such as your Country, City, Organisation Name and your Email.
It is better to store this information you want to pass into your certificate be stored in a config (.cnf) file. Here is an example config file —
[ ca ]
default_ca = ca_default
[ ca_default ]
dir = /home/ayushsuman/path/to/wherever/tls
database = $dir/index.txt
serial = $dir/serial
new_certs_dir = $dir/certs
default_days = 365
default_md = sha256
policy = policy
# Policy dictates what these values in any certificate signed by your root certificate should be.
[ policy ]
countryName = match
stateOrProvinceName = match
organizationName = match
organizationalUnitName = optional
commonName = supplied
emailAddress = optional
############################################################################################
[ req ]
prompt = no
distinguished_name = req_distinguished_name
x509_extensions = v3_ca
string_mask = utf8only
[ req_distinguished_name ]
countryName = IN
stateOrProvinceName = Karnataka
localityName = Bangalore
0.organizationName = Suman
organizationalUnitName = Suman CA
commonName = *.yourdomain.com
emailAddress = your@email.com
[ v3_ca ]
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid:always,issuer
basicConstraints = critical,CA:true
keyUsage = critical,digitalSignature,keyCertSign
nsComment = "OpenSSL Generated Certificate"
Before we move forward, let’s briefly understand the content of this file.
The first section ca_default has to do with the database for all generated certificates signed by this root or CA cert, and some (default) values related to the validity and fingerprint of the certificate. Policy determines what values the signed certificates can have in the respective fields.
In req section, the prompt is set to no, as you don’t get prompted again for the values while generating the certificate. These values are instead mentioned in the req_distinguished_name section. x509_extensions values are mentioned in the v3_ca section.
The v3_ca section has basicConstraints
which specifies whether this certificate is the root (CA) certificate or not. Values marked with critical
means the client must reject the certificate if they do not understand this value.
The field keyUsage
and extendedKeyUsage
is used to specify the use cases of this certificate. The value keyCertSign
specifies that the private key associated with the certificate will be used to sign other certificates. The value digitalSignature
specifies that the private key will be used to sign digital assets other than certificates.
Full details on this section can be found here —
For certs database, create index.txt and serial.
# Create files - index.txt and serial
touch index.txt
echo 01 > serial
Generate the CA certificate using the command below —
# Generate the root certificate from the private key and the configs
openssl req -new -x509 -days 3650 -config your_config_file.cnf -extensions v3_ca -key private/ca-key.pem -out certs/ca-cert.pem
2. Add this Certificate to the Trust Store (Optional)
For your browsers to accept the certificates signed by this CA certificate, add this certificate to your trust store. In Ubuntu, this will work —
# Add this cert to your trust store (for Ubuntu 22.04)
sudo cp certs/ca-cert.pem /usr/local/share/ca-certificates/NAME_YOUR_CA_CERT.crt
sudo update-ca-certificates
It might be that browsers still might reject the certificate. In that case, try adding the CA certificate to your browser’s trust store.
3. Generate a Signed Certificate
Like previously, we will first create the private key. You can do this in one of the directories of your repository. Be sure to include that directory in the .gitignore if you are using git.
# Generate the private key
openssl ecparam -out yourdomain.key -name prime256v1 -genkey
For the certificate, we will be creating a conf file.
[ ca ]
default_ca = ca_default
[ ca_default ]
dir = /home/ayushsuman/path/to/wherever/tls
certs = $dir/certs
database = $dir/index.txt
new_certs_dir = $dir/certs
serial = $dir/serial
default_days = 365
default_md = sha256
############################################################################################
[ req ]
prompt = no
distinguished_name = req_distinguished_name
req_extensions = v3_req
string_mask = utf8only
[ req_distinguished_name ]
countryName = IN
stateOrProvinceName = Karnataka
localityName = Bangalore
0.organizationName = Suman
organizationalUnitName = Suman Dev
commonName = *.yourdomain.com
emailAddress = your@email.com
[ v3_req ]
basicConstraints = critical,CA:false
keyUsage = critical,digitalSignature
extendedKeyUsage = serverAuth
subjectAltName = @alternate_names
nsComment = "OpenSSL Generated Certificate"
[ alternate_names ]
DNS.1 = yourdomain.com
DNS.2 = www.yourdomain.com
DNS.3 = mail.yourdomain.com
DNS.4 = ftp.yourdomain.com
DNS.5 = localhost
DNS.6 = localhost.localdomain
IP.1 = 127.0.0.1
IP.2 = 10.0.2.2 # localhost IP from Android emulators. Only for Android Developers.
Notice this time, the value in the basicConstrains
is CA:false
instead. There is a extendedKeyUsage
field with the value serverAuth
indicating that the private key will be used by a server.
To create a signed certificate, you first need to create a Certificate Signing Request (CSR). CSR is created from the private key. This CSR contains information about the certificate which will be generated and signed and is passed to the root private key (and root certificate) to generate a signed certificate.
Notice, that there is a new section this time in the conf file — alternate_names. This section is important for your certificate. It contains the list of domains and IP addresses which can be the host name of the server. For example, if you are running the server from your localhost, you should have localhost
as one of the DNS.x
values.
If the server is listening with a different hostname that you have not added to your alternate names, your client (say, a browser) may reject the certificate.
# Create Certificate Signing Request
openssl req -new -key yourdomain.key -out yourdomain.csr -sha256 -config config_file.cnf -extensions v3_req
Now, you can create the certificate signed by the root private key. This signature is verifiable by the root certificate.
# Generate Signed Certificate using the private key, configs and the CSR. You also need to specify the root private key and root certifcate for signing.
openssl ca -keyfile /home/ayushsuman/path/to/wherever/tls/private/ca-key.pem -cert /home/ayushsuman/path/to/wherever/tls/certs/ca-cert.pem -in yourdomain.csr -out yourdomain.crt -config yourdomain.cnf -extensions v3_req
That’s it! 🎉 You have generated a private key and a signed certificate that you can use to secure your website.
Remember what was mentioned before, if someone gets access to the tool used to create your signatures, the security of the system fails. This means that you should keep your private key secure, and not accessible by unauthorized people.
Will my certificate support TLS 1.3 or Quic?
Let’s get this straight. Technically, a certificate and the TLS version support are not related. The same certificate can be used by TLS 1.2 or TLS 1.3 (or older TLS/SSL).
To generate a certificate that supports TLS 1.3, you do not need to do anything extra here. Just make sure to update your computer’s or server’s OpenSSL version (or the version of any other SSL library that you might be using).
Further Reading
This marks the end of this article. A little bit about me, I am a developer in love with the process of building and breaking. I use Medium to share my learnings with the community.
I code in Kotlin (and ofc, Java), Go, Rust, JS, Dart and Python, and you can find my projects on Github, or you can find me on Twitter or Linkedin.
.
PS. I love good music. HMU on Twitter to share your playlists, or to get one from me.