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/
# paranoid umask because we are creating private keys
$ umask 077
$ cd /media/$USER/safe_storage
$ mkdir -p example.net.ca/root-ca/{certreqs,certs,crl,newcerts,private}
$ cd example.net.ca/root-ca

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              = sha384
 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              = sha384
 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
 86
 87#
 88# Intermediate CA Certificate Extensions
 89[ intermed-ca_ext ]
 90basicConstraints        = critical, CA:true, pathlen:0
 91keyUsage                = critical, keyCertSign, cRLSign
 92subjectKeyIdentifier    = hash
 93subjectAltName          = @subject_alt_name
 94authorityKeyIdentifier  = keyid:always
 95issuerAltName           = issuer:copy
 96authorityInfoAccess     = @auth_info_access
 97crlDistributionPoints   = crl_dist
 98
 99#
100# CRL Certificate Extensions
101[ crl_ext ]
102authorityKeyIdentifier  = keyid:always
103issuerAltName           = issuer:copy
104
105#
106# Certificate Authorities Alternative Names
107[ subject_alt_name ]
108URI                     = http://ca.example.net/
109email                   = certmaster@example.net
110
111#
112# Name Constraints
113[ name_constraints ]
114permitted;DNS.1         = example.net
115permitted;DNS.2         = example.org
116permitted;DNS.3         = lan
117permitted;DNS.4         = onion
118permitted;email.1       = example.net
119permitted;email.2       = example.org
120
121#
122# Certificate download addresses for the root CA
123[ auth_info_access ]
124caIssuers;URI           = http://ca.example.net/certs/example.net_Root_Certification_Authority.cert.pem
125
126#
127# CRL Download address for the root CA
128[ crl_dist ]
129fullname                = URI:http://ca.example.net/crl/example.net_Root_Certification_Authority.crl
130
131# 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 commands create either a new password protected EC or RSA key and additionally a certificate signing request for the root:

# eliptic curves have smaller key sizes and a similar security level
$ openssl ecparam -genkey -name secp384r1 | openssl ec -aes256 -out private/root-ca.key.pem
read EC key
writing EC key
Enter PEM pass phrase:
Verifying - Enter PEM pass phrase:

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

Or RSA:

$ 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.pem \
    example.net_Root_Certification_Authority.cert.crt
$ sudo dpkg-reconfigure ca-certificates

Install on Android Systems

References