This post details how to setup a root OpenSSL Certificate Authority using Elliptic-curve cryptography (ECC) based on the following two resources.
- OpenSSL Cookbook — Another great resource by Ivan Ristić and it’s FREE!
- Building an OpenSSL Certificate Authority — Multi-post series on the F5 dev/central community by Chase Abbott.
OpenBSD 6.6 GENERIC
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:
- Approve Subordinate CA — used for multi-tier certificate infrastructures.
- 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
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.
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.
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.
AIA and CRL
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.
openssl rand -hex 25 > $BASEPATH$CANAME/private/passphrase
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:
[ 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 ]
Create Private Key
The following command will create a private key and encrypt it.
When prompted, use the password stored in
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 \
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 \
With the CA successfully created, generate a CRL using the following command.
-passin parameter used to specify the password source.
openssl ca -gencrl -config /var/CA/root-ca.cnf \
-out /var/CA/RootCA/RootCA.crl \
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:
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.
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:
# 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 email@example.com:/some/folder/RootCA.crl
To manually launch this script:
It’s important to test that
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
Test cron Script
Consider lowering the seconds value (e.g.
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.