Using Elliptic Curve with an OpenSSL PKI

OpenSSL is a tool that can be used to setup a (simple) PKI, but in its most basic form a command line tool with an endless amount of options. I find myself searching for the correct syntax of OpenSSL to create a new CA, sign a CSR, etc. over and over again. It is very likely that there are quite a few other solutions that work easier, but OpenSSL is practically on every Linux/BSD box. Since I’ve done this with OpenSSL probably a dozen times in the last 8 years or so, it was obvious in retrospect: It is simply to complex to memorize, especially if you’re not in the business of setting up and managing an OpenSSL PKI on a daily basis. This post is here primarily as a reminder and will probably not add anything to existing documents as there are plenty around; some as early as the beginning of this century(!) This here is for me 😉 But if you’re curious to see how to create a CA and digital certificates for your website, mailserver or VPN and don’t want to rely on stock defaults that may have been installed for you, then you may want to read on. I’ll be using Elliptic Curve and SHA512, instead of RSA and SHA1.

If you need certificates for a web server for example, you need a CA. You can buy a certificate from a vendor that owns or resells them from an official CA, but you can also do this yourself with so called self signed certificates if you use your own PKI. Without trying to explain self signed certificates: If you’ve setup a CA that no one knows about, you are the only party that trusts it and as a consequence: anybody that attempts to use a service that is using a certificate signed by it is noted as untrustworthy. If you don’t have a public service, this is not much of a problem.

It is quite common for high secure computing environments to completely seperate equipment from other networks. This is known as air-gapping. Following that rule, the CA should be installed on an air-gapped system, like an old laptop with its networking capabilities disabled. This is important, as the CA is part of the foundation of the security and trustworthiness of sites and related infrastructure that are using certificates that it has signed. The system facilitating the CA is air-gapped, because you want to be absolutely sure that:

– No one but yourself can revoke or sign – and thus create – certificates with your CA.
– No one can tamper with, remove or replace your CA certificate.

Having an air-gapped CA eliminates the majority of network-based attacks, reducing risks to installation media (the CD/DVD used for installation of the CA system), portable media (like USB sticks to transport CSR and certificates, and backup-disks) and social engineering. Quitely disregarding TEMPEST and FM radio or audio stealth communications.

If you manage to cover these risks, then congratulations are in order: Your clients can trust the CA for being a trustworthy authority that has done everything possible to ensure that its clients can rely on secure communication using the certificates that it provided. Although I haven’t found a lot of references on this, I think it should be noted that this also works the other way around: The CA should only sign certificates from clients that it can trust;

– The CA must establish the true identity of a client and
– the client should prove a certain level of security to ensure that the certificate will not be lost or abused. I’m fully aware that this may appear as somewhat redundant for most webservices, but in the case of mutual TLS a connection is made not only if the web server has a valid certificate, but also requires the web browser to have a valid certificate (2-way). Therefor a client certificate is accountable for at least half of the trustworthyness of the connection or session.
– The client should be aware of any procedures in case the security of certificates has been compromised in any way.

In its simplest form, creating and using a CA is actually quite simple:

A. On the CA (air-gapped) system:
1. Create an OpenSSL.cnf configuration file with sane defaults.
2. Setup directories and files.
3. Set environment variables.
4. Create elliptic curve parameters.
5. Create a CA key-pair.

B. At a (secure) client location:
6. Create elliptic curve parameters.
7. Create a CSR and private key.
8. Transport the CSR to the CA.

C. …and on the CA system:
9. Sign the CSR with the CA key.
10. Transport the resulting certificate back to the client.

At this point, the client has a public key that has been signed by the CA, and its private key has been kept private at all times. The same goes for the CA. Voila! Need a certificate! No problem! Here’s how to do this with OpenSSL.

On the CA:
1. Start with a OpenSSL configuration file. This contains the defaults for signing a certificate request and some other magic.

2. Create the following directories and files:

export SSLDIR=$HOME/ca
mkdir $SSLDIR
mkdir $SSLDIR/certs
mkdir $SSLDIR/crl
mkdir $SSLDIR/newcerts
mkdir $SSLDIR/private
mkdir $SSLDIR/requests
touch $SSLDIR/index.txt
echo "01" > $SSLDIR/serial
chmod 700 $SSLDIR

3. Add this line to your .profile or .bashrc -file:

export SSLDIR=$HOME/ca

4. Create elliptic curve parameters:

openssl ecparam -out ec-secp384r1.pem -name secp384r1

The ecparam command as stated above here is not random, but chosen from a list which was produced by:

openssl ecparam -list_curves

5. Create your CA key pair, using EC parameters:

openssl req -config $SSLDIR/openssl.cnf -new -x509 \
            -days 3652 -sha512 -newkey ec:ec-secp384r1.pem \
            -keyout $SSLDIR/private/ca.key -out $SSLDIR/ca.crt \
            -subj '/O=Unnamed IT-Security Corp./OU=Network Security Administration - local CA dept./'

You can verify the result with:

openssl x509 -in $SSLDIR/ca.crt -text

…Which should have a 384 bit EC public key and ECDSA-with-SHA512 signature algorithm, valid for the next 10 years.

On a trusted client:
6. Repeat step 4 as done on the trusted system if you want you a EC certificate, otherwise use rsa:4096 as a value to the -newkey parameter in the command here below in step 7.

7. Create a certificate signing request, including private key:

openssl req -nodes -newkey ec:ec-secp521r1.pem -keyout ${CLIENT}.key -new -out ${CLIENT}.csr

Note that the name of both the CSR and KEY are taken from the CLIENT variable, and the EC parameter chosen here is secp521r1 from the curves list. This certificate should have a shorter lifespan than the CA.

8. Put the CSR – not the private key(!) – on mobile media and take it to the CA.

…And back on the CA:
9. Sign the CSR and place the resulting certificate on mobile media:

openssl ca -config $SSLDIR/openssl.cnf -policy policy_anything \
           -extensions ssl_client -out ${CLIENT}.pem \
           -infiles ${CLIENT}.csr

That command creates a certificate based on the ssl_client specification in the openssl.cnf-file.

Optionally you could convert the certificate to p12 format:

openssl pkcs12 -export -clcerts -in ${CLIENT}.pem \
               -inkey ${CLIENT}.key -out ${CLIENT}.p12

…Or back from P12 to PEM format again:

openssl pkcs12 -in ${CLIENT}.p12 -clcerts -nodes -out ${CLIENT}.crt

Do note that the CRT-file differs from the PEM-file, although both are client certificates (and presumably in PEM format).


In this article I made an assumption: Software is flawed and cryptographic algorithms are “perfect”. That is the primary reason for the air gap and dreadful procedures to get a certificate. But there is another stream of thought: What if software was perfect and crypto algorithms flawed? If the software is perfect, the CA would not be vulnerable to remote exploitation. Then, you could have an online CA that automatically signs requests from and for designated clients. If you need to protect secrets, and if there is no reason for others to acknowledge the authenticity of the CA – if your network is not connected to other networks – then this can be based upon a self signed certificate. The certificates created by that CA would likely have a short lifespan, because an adversary is assumed to break the flawed crypto. In that line of thought, all crypto can be broken given enough time. This cannot be prevented of course, but it could be dealt with if the CA would have an API that would allow different crypto modules to be installed at runtime and issue new certificates to its clients. There are vendors of such solutions, some even add fancy hardware to further strengthen the exclusiveness of secrets (HSM).

The reality is that there is no 100% “perfect” software, and neither are crypto algorithms. Regardless if they are implemented in hard- or software. The poor mans PKI in this article  lives in a less than perfect world, and honestly, using an air-gapped system for the CA is inconvenient at best. I have used a VM with networking capabilities disabled for this in the past, which was less obstructive, although it obviously is less secure than dedicated hardware. Do not think lightly about the location of the CA key(s). Failing to secure them may lead to digital disasters of epic proportions. And every client will surely tell you that they will go through great lengths to ensure the safety and well being of any digital asset, like a precious certificate from your CA. It is my experience that very few clients will actually make the effort of generating a CSR and private key themselves, and most of them will simply ask for both public and private key (totally forgetting about the CA public key for verification). By doing so inherently trusting me with their private key <sigh>. Instead of being a lone and weird security-purist, I wrote a make-cert shell script for CA signing purposes (which is of course wrong).

Last remarks: Elliptic curve certificates are somewhat ‘new’ and may not be a smooth and easy installation where RSA certificates have been tried and tested. Firefox recently updated its certificate validation algorithms which may cause some interoperability issues. OpenVPN is unfortunately tied to RSA certificates for both client and server certificates up to version 2.4.0. But on the bright-side: the CA can use EC, since it is only used to create certificates which is primarily what is described in this article 😉

The only ‘proof’ that EC certificates may actually work – in an apache/webserver alike environment – is with using the openssl s_client command:

openssl s_client -connect -cert ${CLIENT}.crt

When doing so you may see really strong crypto being negiotiated:

New, TLSv1/SSLv3, Cipher is ECDHE-ECDSA-AES256-GCM-SHA384
Server public key is 521 bit
Secure Renegotiation IS supported
Compression: NONE
Expansion: NONE
Protocol  : TLSv1.2
Cipher    : ECDHE-ECDSA-AES256-GCM-SHA384


This entry was posted in Crypto, IT Security, Shell script and tagged , , , , , , , , , , , , , , . Bookmark the permalink.