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

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              = sha384
 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              = sha384
 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 ]
 98keyUsage                = critical, digitalSignature, keyEncipherment
 99extendedKeyUsage        = critical, serverAuth, clientAuth
100subjectKeyIdentifier    = hash
101authorityKeyIdentifier  = keyid:always
102issuerAltName           = issuer:copy
103authorityInfoAccess     = @auth_info_access
104crlDistributionPoints   = crl_dist
105
106#
107# Client Certificate Extensions
108[ client_ext ]
109keyUsage                = critical, digitalSignature
110extendedKeyUsage        = critical, clientAuth
111subjectKeyIdentifier    = hash
112authorityKeyIdentifier  = keyid:always
113issuerAltName           = issuer:copy
114authorityInfoAccess     = @auth_info_access
115crlDistributionPoints   = crl_dist
116
117#
118# User Certificate Extensions
119[ user_ext ]
120keyUsage                = critical, digitalSignature
121extendedKeyUsage        = critical, clientAuth, emailProtection
122subjectKeyIdentifier    = hash
123authorityKeyIdentifier  = keyid:always
124issuerAltName           = issuer:copy
125authorityInfoAccess     = @auth_info_access
126crlDistributionPoints   = crl_dist
127
128#
129# CRL Certificate Extensions
130[ crl_ext ]
131authorityKeyIdentifier  = keyid:always
132issuerAltName           = issuer:copy
133
134#
135# Certificate Authorities Alternative Names
136[ subject_alt_name ]
137URI                     = http://ca.example.net/
138email                   = certmaster@example.net
139
140#
141# Certificate download addresses for the intermediate CA
142[ auth_info_access ]
143caIssuers;URI           = http://ca.example.net/certs/example.net_Intermediate_Certification_Authority.cert.pem
144
145#
146# CRL Download address for the intermediate CA
147[ crl_dist ]
148fullname                = URI:http://ca.example.net/crl/example.net_Intermediate_Certification_Authority.crl
149
150# EOF

Switch the OpenSSL configuration to the Intermediate CA:

$ export OPENSSL_CONF=./intermed-ca.cnf

Generate CSR and new Key

Once again, you may choose between an EC and an RSA 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).

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

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

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

Or RSA:

$ 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.