Creating the Root CA

Directories and Files

The certificate authority uses a specific directory structure to safe keys, signed certificates, singing requests and revocation lists. The directory structure is defind 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
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
#
# OpenSSL configuration for the Root Certification Authority.
#

#
# This definition doesn't work if HOME isn't defined.
CA_HOME                 = .
RANDFILE                = $ENV::CA_HOME/private/.rnd

#
# Default Certification Authority
[ ca ]
default_ca              = root_ca

#
# Root Certification Authority
[ root_ca ]
dir                     = $ENV::CA_HOME
certs                   = $dir/certs
serial                  = $dir/root-ca.serial
database                = $dir/root-ca.index
new_certs_dir           = $dir/newcerts
certificate             = $dir/root-ca.cert.pem
private_key             = $dir/private/root-ca.key.pem
default_days            = 1826 # Five years
crl                     = $dir/root-ca.crl
crl_dir                 = $dir/crl
crlnumber               = $dir/root-ca.crlnum
name_opt                = multiline, align
cert_opt                = no_pubkey
copy_extensions         = copy
crl_extensions          = crl_ext
default_crl_days        = 180
default_md              = sha256
preserve                = no
email_in_dn             = no
policy                  = policy
unique_subject          = no

#
# Distinguished Name Policy for CAs
[ policy ]
countryName             = optional
stateOrProvinceName     = optional
localityName            = optional
organizationName        = supplied
organizationalUnitName  = optional
commonName              = supplied

#
# Root CA Request Options
[ req ]
default_bits            = 4096
default_keyfile         = private/root-ca.key.pem
encrypt_key             = yes
default_md              = sha256
string_mask             = utf8only
utf8                    = yes
prompt                  = no
req_extensions          = root-ca_req_ext
distinguished_name      = distinguished_name
subjectAltName          = @subject_alt_name

#
# Root CA Request Extensions
[ root-ca_req_ext ]
subjectKeyIdentifier    = hash
subjectAltName          = @subject_alt_name

#
# Distinguished Name (DN)
[ distinguished_name ]
organizationName        = example.net
commonName              = example.net Root Certification Authority

#
# Root CA Certificate Extensions
[ root-ca_ext ]
basicConstraints        = critical, CA:true
keyUsage                = critical, keyCertSign, cRLSign
nameConstraints         = critical, @name_constraints
subjectKeyIdentifier    = hash
subjectAltName          = @subject_alt_name
authorityKeyIdentifier  = keyid:always
issuerAltName           = issuer:copy
authorityInfoAccess     = @auth_info_access
crlDistributionPoints   = crl_dist

#
# Intermediate CA Certificate Extensions
[ intermed-ca_ext ]
basicConstraints        = critical, CA:true, pathlen:0
keyUsage                = critical, keyCertSign, cRLSign
subjectKeyIdentifier    = hash
subjectAltName          = @subject_alt_name
authorityKeyIdentifier  = keyid:always
issuerAltName           = issuer:copy
authorityInfoAccess     = @auth_info_access
crlDistributionPoints   = crl_dist

#
# CRL Certificate Extensions
[ crl_ext ]
authorityKeyIdentifier  = keyid:always
issuerAltName           = issuer:copy

#
# Certificate Authorities Alternative Names
[ subject_alt_name ]
URI                     = http://ca.example.net/
email                   = certmaster@example.net

#
# Name Constraints
[ name_constraints ]
permitted;DNS.1         = example.net
permitted;DNS.2         = example.org
permitted;DNS.3         = lan
permitted;DNS.4         = onion
permitted;email.1       = example.net
permitted;email.2       = example.org

#
# Certificate download addresses for the root CA
[ auth_info_access ]
caIssuers;URI           = http://ca.example.net/certs/example.net_Root_Certification_Authority.cert.pem

#
# CRL Download address for the root CA
[ crl_dist ]
fullname                = URI:http://ca.example.net/crl/example.net_Root_Certification_Authority.crl

# 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