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
  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
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
#
# OpenSSL configuration for the Intermediate Certification Authority.
#

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

#
# XMPP address Support
[ new_oids ]
xmppAddr          = 1.3.6.1.5.5.7.8.5
dnsSRV            = 1.3.6.1.5.5.7.8.7

#
# Default Certification Authority
[ ca ]
default_ca              = intermed_ca

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

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

#
# Distinguished Name Policy for Personal Certificates
[ user_policy ]
countryName             = supplied
stateOrProvinceName     = optional
localityName            = supplied
organizationName        = optional
organizationalUnitName  = optional
commonName              = supplied
emailAddress            = supplied
#xmppAddr               = optional # Added to SubjAltName by req

#
# Intermediate CA request options
[ req ]
default_bits            = 3072
default_keyfile         = private/intermed-ca.key
encrypt_key             = yes
default_md              = sha256
string_mask             = utf8only
utf8                    = yes
prompt                  = no
req_extensions          = req_ext
distinguished_name      = distinguished_name
subjectAltName          = subject_alt_name

#
# Intermediate CA Request Extensions
[ req_ext ]
subjectKeyIdentifier    = hash
subjectAltName          = @subject_alt_name

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

#
# Server Certificate Extensions
[ server_ext ]
basicConstraints        = CA:FALSE
keyUsage                = critical, digitalSignature, keyEncipherment
extendedKeyUsage        = critical, serverAuth, clientAuth
subjectKeyIdentifier    = hash
authorityKeyIdentifier  = keyid:always
issuerAltName           = issuer:copy
authorityInfoAccess     = @auth_info_access
crlDistributionPoints   = crl_dist

#
# Client Certificate Extensions
[ client_ext ]
basicConstraints        = CA:FALSE
keyUsage                = critical, digitalSignature
extendedKeyUsage        = critical, clientAuth
subjectKeyIdentifier    = hash
authorityKeyIdentifier  = keyid:always
issuerAltName           = issuer:copy
authorityInfoAccess     = @auth_info_access
crlDistributionPoints   = crl_dist

#
# User Certificate Extensions
[ user_ext ]
basicConstraints        = CA:FALSE
keyUsage                = critical, digitalSignature
extendedKeyUsage        = critical, clientAuth, emailProtection
subjectKeyIdentifier    = hash
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

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

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

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