Image by David Krüger from Pixabay

OpenSSL Root Certificate Authority

6 min readJan 18, 2021

This post details how to set up a root OpenSSL Certificate Authority using Elliptic-curve cryptography (ECC) based on the following two resources.

Update — 2021–7–28

After further testing and learning more nuances of running a PKI, this post has been significantly updated.

Tested using:

  • OpenBSD 6.6 GENERIC
  • LibreSSL 3.0.2

Root Certificate Authority Configuration

The greatest hurdle to building out a certificate infrastructure is planning. One must understand how certificates will be used short term and long term as both will impact the overall design. Much planning is centered around how certificates are issued to users/services. Not only the type of certificate being distributed but also how it gets issued from the certificate infrastructure. For this is why certificate service providers have many levels of CAs in the certificate chain. Each tier is used to enforce a policy for delegating how each type of certificate is issued. Thankfully, setting up a root CA is fairly straightforward since it will often serve one of two roles:

  1. Approve Subordinate CA — used for multi-tier certificate infrastructures.
  2. Distribute Certificates — common in small networks where the root CA also distributes certificates.

This post will cover a root CA used to approve one or more subordinate CAs. This is often referred to as an offline root CA though not entirely offline as it must periodically publish a Certificate Revocation List (CRL). The following table shows the desired properties of the root CA.

              Desired Root CA Certificate Properties 
Algorithm: secp521r1
CRL Published Interval: 90 days
Signature Hash Algorithm: SHA512
Specific "Valid from": January 1 00:00:00 1970 GMT
Specific "Valid to": December 31 23:59:59 9999 GMT
AIA and CRL locations:
Online Certificate Status Protocol (OCSP): Not used
basicConstraints: critical,CA:true,pathlen:1

Some points worth mentioning in regards to the desired properties of the Root CA.


Many docs and how-tos will use P384. This could be because P521 isn’t listed in NSA suite B or compatibility with an existing application. Whatever the case, be sure to test your own environment before using any ECC algorithms.

90 day CRL

Is the maximum amount of time an invalid certificate will be trusted on the network. Since this will be a root CA, a revoked Issuing CA certificate will be trusted for a maximum of 90 days. Getting around this limitation requires manually updating all clients using this certificate infrastructure. Choose a value that works best.

Validity Dates

Choose whatever dates you want. This post should be considered a proof-of-concept rather than a guide for an enterprise production-quality CA. If that were the case, there would have been much discussion about protecting the private key, Hardware Security Modules (HSM) and similar devices, backups, auditing, etc. Thus the seemingly nonsensical range.


This should be a website domain dedicated to hosting certificates and CRLs supporting the certificate infrastructure. No special modules are needed, just serve these files as static content and ensure the MIME type is properly configured.

application/pkix-crl                .crl
application/x-x509-ca-cert .crt .der

Ideally, there should be a web server hosting this site on your LAN and another on the public internet. This allows these critical PKI files to be available to internal and external hosts. Note that this will require some form of split-horizon DNS.


OCSP was designed for certificate infrastructures with large CRLs. Instead of clients downloading these large files, they issue an HTTP request to the OCSP service specifying the certificate’s serial number. The OCSP service checks the serial number against the same CRL the client would have downloaded. Making OCSP only as current as the CRL. The response is created and signed, using an OCSP signing certificate, before being sent back to the client. As you can see, this is a great solution for large service providers since each OCSP request/response is very small.

Hosting the OCSP service has administrative overhead. In Windows, it runs as an ASP script using a .NET CLR while other platforms use a CGI-based solution. Both require an OCSP signing certificate and a copy of the latest CRL.

This deployment will not have large CRLs and thus doesn’t justify the amount of administration required to support OCSP.


This section of the configuration is very important for a Root Certificate Authority. First and foremost, any CA that will be issuing certificates must have CA:true as this setting designates it as an Issuing CA. On a related note, all client certificates should have this property set to false to prevent it from being used to issue certs. Taken from the openssl man page:

A CA certificate must include the basicConstraints value with the CA field set to TRUE. An end user certificate must either set CA to FALSE or exclude the extension entirely. Some software may require the inclusion of basicConstraints with CA set to FALSE for end entity certificates.

The pathlen property defines how many levels are in the PKI hierarchy. Since there will be just one Issuing CA below this Root CA, the property will be configured pathlen:1. If one were to incorporate another Issuing CA into the hierarchy (i.e. not scaling horizontally), it would require incrementing this value which is why planning PKI rollout is so important. Explanation from the openssl man page:

The pathlen parameter indicates the maximum number of CAs that can appear below this one in a chain. So if you have a CA with a pathlen of zero it can only be used to sign end user certificates and not further CAs.

Create Folder Structure

The following commands create the folder structure and necessary files to get started.

Note that the password to protect the private key is documented during this step. If a specific password is required be sure to update private/passphrase as it is used in later commands.

Create Root CA OpenSSL Configuration File

OpenSSL needs a configuration file for the subsequent commands. The following is a product of the reference material mentioned at the beginning of this post.

Update the necessary settings within this file to reflect your environment then save it to: $BASEPATH/$CANAME/$CANAME.cnf

Create Private Key

Create a private key and encrypt it.

openssl ecparam -genkey \
-name secp521r1 | openssl ec \
-aes256 -out $BASEPATH/$CANAME/private/$CANAME.key \
-passout file:$BASEPATH/$CANAME/private/passphrase

Create Certificate Signing Request (CSR)

Create a CSR using settings from the configuration file.

openssl req -config $BASEPATH/$CANAME/$CANAME.cnf \
-new -extensions ca_ext \
-key $BASEPATH/$CANAME/private/$CANAME.key \
-passin file:$BASEPATH/$CANAME/private/passphrase

Approve CSR

When approving the CSR, values specified in this command will override values in the configuration file and CSR. If successful, the new certificate will be displayed followed by a "y/n" prompt to confirm approval. Answer in the affirmative to continue.

openssl ca -config $BASEPATH/$CANAME/$CANAME.cnf -selfsign \
-keyfile $BASEPATH/$CANAME/private/$CANAME.key \
-startdate 19700101000000Z \
-enddate 99991231235959Z \
-out $BASEPATH/$CANAME/certs/$CANAME.crt \
-extensions ca_ext \
-passin file:$BASEPATH/$CANAME/private/passphrase

Generate CRL

With the CA successfully created, generate a CRL.

openssl ca -gencrl -config $BASEPATH/$CANAME/$CANAME.cnf \
-passin file:$BASEPATH/$CANAME/private/passphrase

Publish Certificate

At this point, the website should be available and able to serve content. Copy /var/CA/RootCA/certs/RootCA.crt to the website content directory so it can be accessed via:

This website does not need HTTPS. In fact, it should only be used with HTTP over TCP port 80. This is because the certificate has been digitally signed which protects it against any tampering.

Publish CRL

Publishing a CRL is tricky since cron doesn’t offer a way to run a task every N days or any other unit of time. To get around this, the following script is used. It first generates a CRL then parses it to determine when to reschedule. Since a minute is the smallest unit, 60 seconds are subtracted to ensure the CRL is updated just before expiration.

Update paths in the script to reflect your environment. Specifically the scp command which copies the CRL to the content directory.

Save the script to: /var/CA/$

Now, manually launch this script which will then schedule itself.

/bin/sh /var/CA/

Fix Permissions

Use the following commands to fix permissions. They could even be incorporated into a script that gets invoked by daily or whenever CA tasks are performed.


chmod 700 $BASEPATH
chmod 700 $BASEPATH/$
chmod -R 700 $BASEPATH/$CANAME
find $BASEPATH/$CANAME -type f -print0 | xargs -0 chmod 600
chmod 400 $BASEPATH/$CANAME/private/*


It’s important to test that RootCA.crt and RootCA.crl are available.

Download Files

Verify they’re accessible via HTTP over TCP port 80.

ftp -o RootCA.crl
ftp -o RootCA.crt

View Files

Review the certificate and CRL.

openssl x509 -text -noout -in RootCA.crt
openssl crl -text -noout -in RootCA.crl

Test cron Script

Verify the next scheduled launch is correctly set.