Image by David Krüger from Pixabay

OpenSSL Root Certificate Authority

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

Tested with:

  • 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 just the type of certificate being distributed but more importantly how it gets issued from the certificate infrastructure. That is why certificate service providers will 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 straight forward 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: pki.domain.com
Online Certificate Status Protocol (OCSP): Not used

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 enviornment before using any ECC algorithms.

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.

Choose whatever dates you want. This post should be considered a proof-of-concept rather than a guide for a production quality CA. For production quality, there would have been much discussion about protecting the private key, Hardware Security Module (HSM) or similar device, backups, auditing, etc. Thus the seemingly nonsensical range.

This should be a website dedicated to hosting certificates and CRLs supporting the certificate infrastructure. Ideally there should be a web server hosting this site on your LAN and another on the public internet. This way your PKI is still available to internal hosts if connectivity is severed while external clients/services have the necessary files when outside the LAN. 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 it.

If this feature is of interest, update the necssary .cnf files with the OCSP service locations and then setup the OCSP websites.

Create Folder Structure

The following creates 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 will be used later for automating CRL publishing.

CANAME=”RootCA”
BASEPATH=”/var/CA/”
mkdir $BASEPATH
mkdir $BASEPATH$CANAME
mkdir $BASEPATH$CANAME/certs
mkdir $BASEPATH$CANAME/db
mkdir $BASEPATH$CANAME/private
openssl rand -hex 25 > $BASEPATH$CANAME/private/passphrase
touch $BASEPATH$CANAME/db/index
openssl rand -hex 16 > $BASEPATH$CANAME/db/serial
echo 1001 > $BASEPATH/$CANAME/db/crlnumber
chmod -R 700 $BASEPATH$CANAME
chmod 400 $BASEPATH$CANAME/private/passphrase

Create Root CA OpenSSL Configuration File

OpenSSL needs a configuration file for the subsequent commands. The following is a product of the previously mentioned resources.

Update the necessary settings within this file to reflect your environment then save it to: /var/CA/root-ca.cnf

[ default ]
name = RootCA
domain_suffix = domain.com
aia_url = http://pki.domain.com/$name.crt
crl_url = http://pki.domain.com/$name.crl
default_ca = ca_default
name_opt = utf8,esc_ctrl,multiline,lname,align
[ ca_dn ]
organizationName = “domain.com”
commonName = $name
[ ca_default ]
home = /var/CA/$name
database = $home/db/index
serial = $home/db/serial
crlnumber = $home/db/crlnumber
certificate = $home/certs/$name.crt
private_key = $home/private/$name.key
RANDFILE = $home/private/.RAND
new_certs_dir = $home/certs
unique_subject = no
copy_extensions = none
default_days = 7300
default_crl_days = 90
default_md = sha512
policy = policy_c_o_match
name = $name
[ policy_c_o_match ]
countryName = optional
stateOrProvinceName = optional
organizationName = match
organizationalUnitName = optional
commonName = supplied
emailAddress = optional
[ req ]
encrypt_key = yes
default_md = sha512
utf8 = yes
string_mask = utf8only
prompt = no
distinguished_name = ca_dn
req_extensions = ca_ext
[ ca_ext ]
basicConstraints = critical,CA:true
keyUsage = critical,keyCertSign,cRLSign
subjectKeyIdentifier = hash
[ sub_ca_ext ]
authorityInfoAccess = @issuer_info
authorityKeyIdentifier = keyid:always
basicConstraints = critical,CA:true,pathlen:0
crlDistributionPoints = @crl_info
extendedKeyUsage = clientAuth,serverAuth
keyUsage = critical,keyCertSign,cRLSign
subjectKeyIdentifier = hash
[ crl_info ]
URI.0 = http://pki.domain.com/$name.crl
[ issuer_info ]
caIssuers;URI.0 = http://pki.domain.com/$name.crt
[ name_constraints ]
permitted;DNS.0=domain.com
permitted;DNS.0=*.domain.com
excluded;IP.0=0.0.0.0/0.0.0.0

Create Private Key

The following command will create a private key and encrypt it.

When prompted, use the password stored in /var/CA/RootCA/private/passphrase

openssl ecparam -genkey \
-name secp521r1 | openssl ec \
-aes256 -out RootCA/private/RootCA.key

Create Certificate Signing Request (CSR)

Create a CSR using settings from the configuration file and newly created private key.

openssl req -config /var/CA/root-ca.cnf \
-new -extensions ca_ext \
-key /var/CA/RootCA/private/RootCA.key \
-out /var/CA/RootCA/RootCA.csr

Approve CSR

Using the following command, approve the CSR. Values specified in the cli override values in the configuration file. If successful, the new certificate will be displayed followed by a "y/n" to confirm approval. Answer in the affirmative to continue on.

openssl ca -config /var/CA/root-ca.cnf -selfsign \
-keyfile /var/CA/RootCA/private/RootCA.key \
-startdate 19700101000000Z \
-enddate 99991231235959Z \
-out /var/CA/RootCA/certs/RootCA.crt \
-in /var/CA/RootCA/RootCA.csr \
-extensions ca_ext

Generate CRL

With the CA successfully created, generate a CRL using the following command.

Note the -passin parameter used to specify the password source.

openssl ca -gencrl -config /var/CA/root-ca.cnf \
-out /var/CA/RootCA/RootCA.crl \
-passin file:/var/CA/RootCA/private/passphrase

Publish Certificate

At this point, the website pki.domain.com 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: http://pki.domain.com/RootCA.crt

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. To get around this, the following script is used. First it schedules the next launch then performs CRL related tasks.

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

Save the script to: /var/CA/RootCA-publish-crl.sh

#!/bin/sh
# save crontab to file removing the entry
# of the currently running script
/usr/bin/crontab -l | grep -v "/var/CA/RootCA-publish-crl.sh" > /var/CA/tmpCron
# add new entry just short of 90 days
/bin/echo $(date -j -r `expr $(date +%s) + 7775938` "+%M %H %d %m")" * /bin/sh /var/CA/RootCA-publish-crl.sh 2>&1" >> /var/CA/tmpCron
# load updated crontab
/usr/bin/crontab /var/CA/tmpCron
# remove temporary cron file
/bin/rm /var/CA/tmpCron
# generate new CRL
/usr/bin/openssl ca -gencrl -config /var/CA/root-ca.cnf -out /var/CA/RootCA/RootCA.crl -passin file:/var/CA/RootCA/private/passphrase
# copy crl to website content directory
/usr/bin/scp -r -p -4 -i /pki.domain.com.key /var/CA/RootCA/RootCA.crl some-user@pki.domain.com:/some/folder/RootCA.crl

To manually launch this script:

/bin/sh /var/CA/RootCA-publish-crl.sh

Test

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

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

ftp -o RootCA.crl http://pki.domain.com/RootCA.crl
ftp -o RootCA.crt http://pki.domain.com/RootCA.crt

Review the certificate and CRL.

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

Consider lowering the seconds value (e.g. 7775938) to 120 seconds for testing. This is a great way to ensure a new CRL is properly generated and copied to the pki.domain.com content directory.

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store