Creating the Intermediate 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 defined within the OpenSSL configuration file:

intermed-ca/
    |
    |----certreqs/
    |
    |----certs/
    |
    |----crl/
    |
    |----newcerts/
    |
    |----private/
$ cd /media/$USER/safe_storage
$ mkdir -p example.net.ca/intermed-ca/{certreqs,certs,crl,newcerts,private}
$ cd example.net.ca/intermed-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 intermed-ca.index
$ echo 00 > intermed-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 > intermed-ca.serial

OpenSSL configuration

The configuration for the intermediate certificate authority is in the file intermed-ca/intermed-ca.cnf.

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

  1#
  2# OpenSSL configuration for the Intermediate Certification Authority.
  3#
  4
  5#
  6# This definition doesn't work if HOME isn't defined.
  7CA_HOME                 = .
  8RANDFILE                = $ENV::CA_HOME/private/.rnd
  9oid_section             = new_oids
 10
 11#
 12# XMPP address Support
 13[ new_oids ]
 14xmppAddr          = 1.3.6.1.5.5.7.8.5
 15dnsSRV            = 1.3.6.1.5.5.7.8.7
 16
 17#
 18# Default Certification Authority
 19[ ca ]
 20default_ca              = intermed_ca
 21
 22#
 23# Intermediate Certification Authority
 24[ intermed_ca ]
 25dir                     = $ENV::CA_HOME
 26certs                   = $dir/certs
 27serial                  = $dir/intermed-ca.serial
 28database                = $dir/intermed-ca.index
 29new_certs_dir           = $dir/newcerts
 30certificate             = $dir/intermed-ca.cert.pem
 31private_key             = $dir/private/intermed-ca.key.pem
 32default_days            = 396 # 1 year + 31 days
 33crl                     = $dir/crl/intermed-ca.crl
 34crl_dir                 = $dir/crl
 35crlnumber               = $dir/intermed-ca.crlnum
 36name_opt                = multiline, align
 37cert_opt                = no_pubkey
 38copy_extensions         = copy
 39crl_extensions          = crl_ext
 40default_crl_days        = 30
 41default_md              = sha256
 42preserve                = no
 43email_in_dn             = no
 44policy                  = policy
 45unique_subject          = no
 46
 47#
 48# Distinguished Name Policy
 49[ policy ]
 50countryName             = optional
 51stateOrProvinceName     = optional
 52localityName            = optional
 53organizationName        = optional
 54organizationalUnitName  = optional
 55commonName              = supplied
 56
 57#
 58# Distinguished Name Policy for Personal Certificates
 59[ user_policy ]
 60countryName             = supplied
 61stateOrProvinceName     = optional
 62localityName            = supplied
 63organizationName        = optional
 64organizationalUnitName  = optional
 65commonName              = supplied
 66emailAddress            = supplied
 67#xmppAddr               = optional # Added to SubjAltName by req
 68
 69#
 70# Intermediate CA request options
 71[ req ]
 72default_bits            = 3072
 73default_keyfile         = private/intermed-ca.key
 74encrypt_key             = yes
 75default_md              = sha256
 76string_mask             = utf8only
 77utf8                    = yes
 78prompt                  = no
 79req_extensions          = req_ext
 80distinguished_name      = distinguished_name
 81subjectAltName          = subject_alt_name
 82
 83#
 84# Intermediate CA Request Extensions
 85[ req_ext ]
 86subjectKeyIdentifier    = hash
 87subjectAltName          = @subject_alt_name
 88
 89#
 90# Distinguished Name (DN)
 91[ distinguished_name ]
 92organizationName        = example.net
 93commonName              = example.net Intermediate Certification Authority
 94
 95#
 96# Server Certificate Extensions
 97[ server_ext ]
 98basicConstraints        = CA:FALSE
 99keyUsage                = critical, digitalSignature, keyEncipherment
100extendedKeyUsage        = critical, serverAuth, clientAuth
101subjectKeyIdentifier    = hash
102authorityKeyIdentifier  = keyid:always
103issuerAltName           = issuer:copy
104authorityInfoAccess     = @auth_info_access
105crlDistributionPoints   = crl_dist
106
107#
108# Client Certificate Extensions
109[ client_ext ]
110basicConstraints        = CA:FALSE
111keyUsage                = critical, digitalSignature
112extendedKeyUsage        = critical, clientAuth
113subjectKeyIdentifier    = hash
114authorityKeyIdentifier  = keyid:always
115issuerAltName           = issuer:copy
116authorityInfoAccess     = @auth_info_access
117crlDistributionPoints   = crl_dist
118
119#
120# User Certificate Extensions
121[ user_ext ]
122basicConstraints        = CA:FALSE
123keyUsage                = critical, digitalSignature
124extendedKeyUsage        = critical, clientAuth, emailProtection
125subjectKeyIdentifier    = hash
126authorityKeyIdentifier  = keyid:always
127issuerAltName           = issuer:copy
128authorityInfoAccess     = @auth_info_access
129crlDistributionPoints   = crl_dist
130
131#
132# CRL Certificate Extensions
133[ crl_ext ]
134authorityKeyIdentifier  = keyid:always
135issuerAltName           = issuer:copy
136
137#
138# Certificate Authorities Alternative Names
139[ subject_alt_name ]
140URI                     = http://ca.example.net/
141email                   = certmaster@example.net
142
143#
144# Certificate download addresses for the intermediate CA
145[ auth_info_access ]
146caIssuers;URI           = http://ca.example.net/certs/example.net_Intermediate_Certification_Authority.cert.pem
147
148#
149# CRL Download address for the intermediate CA
150[ crl_dist ]
151fullname                = URI:http://ca.example.net/crl/example.net_Intermediate_Certification_Authority.crl
152
153# EOF

Switch the OpenSSL configuration to the Intermediate CA:

$ export OPENSSL_CONF=./intermed-ca.cnf

Generate CSR and new Key

The RSA private key of the intermediate signing certificate needs to be 3072 bits strong. As this is considered safe for the next 8 years (up to 2023). It also should be made write-protected and private and have a strong password.

Make a new Certificate Signing Request (CSR) for the intermediate signing authority:

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

You should find the key in private/intermed-ca.key.pem and the CSR in intermed-ca.req.pem.

Protect the private key:

$ chmod 400 private/intermed-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 intermediate using an already existing key:

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

You can peek in to the CSR:

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

If everything looks alright, copy the CSR to the Root CA for later signing:

$ cp intermed-ca.req.pem  \
    /media/$USER/safe_storage/example.net.ca/root-ca/certreqs/

Sign the Intermediate with the Root CA

Change your working directory to the where the Root CA stores its files:

$ cd /media/$USER/safe_storage/example.net.ca/root-ca

Switch the OpenSSL configuration back to the root CA:

$ export OPENSSL_CONF=./root-ca.cnf

Sign the intermediate CSR with the root key for the next 5 years using the intermediate extensions:

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

You can peek in to the signed certificate:

$ openssl x509 -in certs/intermed-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 \
    certs/intermed-ca.cert.pem
certs/intermed-ca.cert.pem: OK

If the certificate looks as expected, copy it to the Intermediate CA directory:

$ cp certs/intermed-ca.cert.pem \
    /media/$USER/safe_storage/example.net.ca/intermed-ca/

Revocation List (CRL)

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

The Intermediate CA is expected to publish revocations of any server or client certificate that has been issued by it.

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 Intermediate CA.

Change your working directory to the where the Intermediate CA stores its files:

$ cd /media/$USER/safe_storage/example.net.ca/intermed-ca

Switch the OpenSSL configuration back to the Intermediate CA:

$ export OPENSSL_CONF=./intermed-ca.cnf

Create the Intermediate CA certificate revocation list (CRL):

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

Install and use the Intermediate CA

Copy the Intermediate CA files to your working environment:

cp /media/$USER/safe_storage/example.net.ca/intermed-ca ~/

Unmount and lock the safe storage and lock the storage medium away.