Creating the Root CA

Directories and Files

The certificate authority uses a specific directory structure to save keys, signed certificates, signing requests and revocation lists. The directory structure is defined within the OpenSSL configuration file.

root-ca/
    |
    |----certreqs/
    |
    |----certs/
    |
    |----crl/
    |
    |----newcerts/
    |
    |----private/
$ cd /media/$USER/safe_storage
$ mkdir -p example.net.ca/root-ca/{certreqs,certs,crl,newcerts,private}
$ cd example.net.ca/root-ca

The directory used to store private keys should not be accessible to others:

$ chmod 700 private

Some data files are needed to keep track of issued certificates, their serial numbers and revocations.

$ touch root-ca.index
$ echo 00 > root-ca.crlnum

Create the serial file, to store next incremental serial number.

Using random instead of incremental serial numbers is a recommended security practice.

$ openssl rand -hex 16 > root-ca.serial

OpenSSL configuration

The OpenSSL configuration for the root certificate authority is in the file root-ca/root-ca.cnf.

The complete configuration is available for download as root-ca.cnf.

  1#
  2# OpenSSL configuration for the Root Certification Authority.
  3#
  4
  5#
  6# This definition doesn't work if HOME isn't defined.
  7CA_HOME                 = .
  8RANDFILE                = $ENV::CA_HOME/private/.rnd
  9
 10#
 11# Default Certification Authority
 12[ ca ]
 13default_ca              = root_ca
 14
 15#
 16# Root Certification Authority
 17[ root_ca ]
 18dir                     = $ENV::CA_HOME
 19certs                   = $dir/certs
 20serial                  = $dir/root-ca.serial
 21database                = $dir/root-ca.index
 22new_certs_dir           = $dir/newcerts
 23certificate             = $dir/root-ca.cert.pem
 24private_key             = $dir/private/root-ca.key.pem
 25default_days            = 1826 # 5 years
 26crl                     = $dir/root-ca.crl
 27crl_dir                 = $dir/crl
 28crlnumber               = $dir/root-ca.crlnum
 29name_opt                = multiline, align
 30cert_opt                = no_pubkey
 31copy_extensions         = copy
 32crl_extensions          = crl_ext
 33default_crl_days        = 180
 34default_md              = sha256
 35preserve                = no
 36email_in_dn             = no
 37policy                  = policy
 38unique_subject          = no
 39
 40#
 41# Distinguished Name Policy for CAs
 42[ policy ]
 43countryName             = optional
 44stateOrProvinceName     = optional
 45localityName            = optional
 46organizationName        = supplied
 47organizationalUnitName  = optional
 48commonName              = supplied
 49
 50#
 51# Root CA Request Options
 52[ req ]
 53default_bits            = 4096
 54default_keyfile         = private/root-ca.key.pem
 55encrypt_key             = yes
 56default_md              = sha256
 57string_mask             = utf8only
 58utf8                    = yes
 59prompt                  = no
 60req_extensions          = root-ca_req_ext
 61distinguished_name      = distinguished_name
 62subjectAltName          = @subject_alt_name
 63
 64#
 65# Root CA Request Extensions
 66[ root-ca_req_ext ]
 67subjectKeyIdentifier    = hash
 68subjectAltName          = @subject_alt_name
 69
 70#
 71# Distinguished Name (DN)
 72[ distinguished_name ]
 73organizationName        = example.net
 74commonName              = example.net Root Certification Authority
 75
 76#
 77# Root CA Certificate Extensions
 78[ root-ca_ext ]
 79basicConstraints        = critical, CA:true
 80keyUsage                = critical, keyCertSign, cRLSign
 81nameConstraints         = critical, @name_constraints
 82subjectKeyIdentifier    = hash
 83subjectAltName          = @subject_alt_name
 84authorityKeyIdentifier  = keyid:always
 85issuerAltName           = issuer:copy
 86authorityInfoAccess     = @auth_info_access
 87crlDistributionPoints   = crl_dist
 88
 89#
 90# Intermediate CA Certificate Extensions
 91[ intermed-ca_ext ]
 92basicConstraints        = critical, CA:true, pathlen:0
 93keyUsage                = critical, keyCertSign, cRLSign
 94subjectKeyIdentifier    = hash
 95subjectAltName          = @subject_alt_name
 96authorityKeyIdentifier  = keyid:always
 97issuerAltName           = issuer:copy
 98authorityInfoAccess     = @auth_info_access
 99crlDistributionPoints   = crl_dist
100
101#
102# CRL Certificate Extensions
103[ crl_ext ]
104authorityKeyIdentifier  = keyid:always
105issuerAltName           = issuer:copy
106
107#
108# Certificate Authorities Alternative Names
109[ subject_alt_name ]
110URI                     = http://ca.example.net/
111email                   = certmaster@example.net
112
113#
114# Name Constraints
115[ name_constraints ]
116permitted;DNS.1         = example.net
117permitted;DNS.2         = example.org
118permitted;DNS.3         = lan
119permitted;DNS.4         = onion
120permitted;email.1       = example.net
121permitted;email.2       = example.org
122
123#
124# Certificate download addresses for the root CA
125[ auth_info_access ]
126caIssuers;URI           = http://ca.example.net/certs/example.net_Root_Certification_Authority.cert.pem
127
128#
129# CRL Download address for the root CA
130[ crl_dist ]
131fullname                = URI:http://ca.example.net/crl/example.net_Root_Certification_Authority.crl
132
133# EOF

Make sure that the OpenSSL configuration file for the root CA is active:

$ export OPENSSL_CONF=./root-ca.cnf

Generate CSR and new Key

The following command creates a new key and a certificate signing request for the root:

$ openssl req -new -out root-ca.req.pem
Generating a 4096 bit RSA private key
.......................................................................++
.......................................................................++
.......................................................................++
.......................................................................++
writing new private key to 'private/root-ca.key.pem'
Enter PEM pass phrase: ********
Verifying - Enter PEM pass phrase: ********
-----

You will find the key in private/root-ca.key and the CSR in root- ca.csr.

Protect the private key:

$ chmod 400 private/root-ca.key.pem

Generate CSR from existing Key

If you need to update existing CA certificates you can use the already existing key to create a new CSR.

The following command creates a certificate signing request for the root using an already existing key:

$ openssl req -new \
    -key private/root-ca.key.pem \
    -out root-ca.req.pem
Enter pass phrase for private/root-ca.key.pem: ********

Show the CSR

You can peek in to the CSR:

$ openssl req -verify -in root-ca.req.pem \
    -noout -text \
    -reqopt no_version,no_pubkey,no_sigdump \
    -nameopt multiline

Self-Signing the Root Certificate

If everything looks ok, self-sign your own request:

$ openssl rand -hex 16 > root-ca.serial
$ openssl ca -selfsign \
    -in root-ca.req.pem \
    -out root-ca.cert.pem \
    -extensions root-ca_ext \
    -startdate `date +%y%m%d000000Z -u -d -1day` \
    -enddate `date +%y%m%d000000Z -u -d +10years+1day`

The signature will be valid for the next ten years.

View it with the following command:

$ openssl x509 -in ./root-ca.cert.pem \
    -noout -text \
    -certopt no_version,no_pubkey,no_sigdump \
    -nameopt multiline

You can verify if it will be recognized as valid:

$ openssl verify -verbose -CAfile root-ca.cert.pem \
    root-ca.cert.pem
root-ca.cert.pem: OK

Revocation List (CRL)

The Root CA publishes Certificate Revocation Lists at regular intervals or when a certificate has been revoked.

The Root CA is expected to only issue revocations of its own self-signed certificate or the Intermediate CA certificate.

There have not been any revocations yet, but still clients and servers verifying any of our certificates will request an up-to-date CRL from the web-address published in the certificates.

We therefore need to create initial, albeit empty CRLs of the Root CA:

$ openssl ca -gencrl -out crl/root-ca.crl

Install As Trusted CA

To be recognized by your systems, devices and software, the Root Certificate needs to be installed as a trusted CA on all Systems.

Install on Ubuntu Linux Systems

Steps to re-include the root certificate in Ubuntu as trusted CA:

$ sudo mkdir -p /usr/local/share/ca-certificates/example.net
$ cd /usr/local/share/ca-certificates/example.net/
$ sudo cp ./root-ca.crt \
    example.net_Root_Certification_Authority.cert.pem
$ sudo dpkg-reconfigure ca-certificates