1.5.2 Creating a Root CA
Creating a new CA involves several steps: configuration, creation of a directory structure and initialization of the key files, and finally generation of the root key and certificate. This section describes the process as well as the common CA operations.
1.5.2.1 Root CA Configuration
Before we can actually create a CA, we need to prepare a configuration file (root-ca.conf
) that will tell OpenSSL exactly how we want things set up. Configuration files aren’t needed most of the time, during normal usage, but they are essential when it comes to complex operations, such as root CA creation. OpenSSL configuration files are powerful; before you proceed I suggest that you familiarize yourself with their capabilities (man config
on the command line).
The first part of the configuration file contains some basic CA information, such as the name and the base URL, and the components of the CA’s distinguished name. Because the syntax is flexible, information needs to be provided only once:
[default]
name = root-ca
domain_suffix = example.com
aia_url = http://$name.$domain_suffix/$name.crt
crl_url = http://$name.$domain_suffix/$name.crl
ocsp_url = http://ocsp.$name.$domain_suffix:9080
default_ca = ca_default
name_opt = utf8,esc_ctrl,multiline,lname,align
[ca_dn]
countryName = "GB"
organizationName = "Example"
commonName = "Root CA"
The second part directly controls the CA’s operation. For full information on each setting, consult the documentation for the ca
command (man ca
on the command line). Most of the settings are self-explanatory; we mostly tell OpenSSL where we want to keep our files. Because this root CA is going to be used only for the issuance of subordinate CAs, I chose to have the certificates valid for 10 years. For the signature algorithm, the secure SHA256 is used by default.
The default policy (policy_c_o_match
) is configured so that all certificates issued from this CA have the countryName
and organizationName
fields that match that of the CA. This wouldn’t be normally done by a public CA, but it’s appropriate for a private CA:
[ca_default]
home = .
database = $home/db/index
serial = $home/db/serial
crlnumber = $home/db/crlnumber
certificate = $home/$name.crt
private_key = $home/private/$name.key
RANDFILE = $home/private/random
new_certs_dir = $home/certs
unique_subject = no
copy_extensions = none
default_days = 3650
default_crl_days = 365
default_md = sha256
policy = policy_c_o_match
[policy_c_o_match]
countryName = match
stateOrProvinceName = optional
organizationName = match
organizationalUnitName = optional
commonName = supplied
emailAddress = optional
The third part contains the configuration for the req
command, which is going to be used only once, during the creation of the self-signed root certificate. The most important parts are in the extensions: the basicConstraints
extension indicates that the certificate is a CA, and keyUsage
contains the appropriate settings for this scenario:
[req]
default_bits = 4096
encrypt_key = yes
default_md = sha256
utf8 = yes
string_mask = utf8only
prompt = no
distinguished_name = ca_dn
req_extensions = ca_ext
[ca_ext]
basicConstraints = critical,CA:true
keyUsage = critical,keyCertSign,cRLSign
subjectKeyIdentifier = hash
The fourth part of the configuration file contains information that will be used during the construction of certificates issued by the root CA. All certificates will be CAs, as indicated by the basicConstraints
extension, but we set pathlen
to zero, which means that further subordinate CAs are not allowed.
All subordinate CAs are going to be constrained, which means that the certificates they issue will be valid only for a subset of domain names and restricted uses. First, the extendedKeyUsage
extension specifies only clientAuth
and serverAuth
, which is TLS client and server authentication. Second, the nameConstraints
extension limits the allowed hostnames only to example.com
and example.org
domain names. In theory, this setup enables you to give control over the subordinate CAs to someone else but still be safe in knowing that they can’t issue certificates for arbitrary hostnames. If you wanted, you could restrict each subordinate CA to a small domain namespace. The requirement to exclude the two IP address ranges comes from the CA/Browser Forum’s Baseline Requirements, which have a definition for technically constrained subordinate CAs.1
In practice, name constraints are not entirely practical, because some major platforms don’t currently recognize the nameConstraints
extension. If you mark this extension as critical, such platforms will reject your certificates. You won’t have such problems if you don’t mark it as critical (as in the example), but then some other platforms won’t enforce it.
[sub_ca_ext]
authorityInfoAccess = @issuer_info
authorityKeyIdentifier = keyid:always
basicConstraints = critical,CA:true,pathlen:0
crlDistributionPoints = @crl_info
extendedKeyUsage = clientAuth,serverAuth
keyUsage = critical,keyCertSign,cRLSign
nameConstraints = @name_constraints
subjectKeyIdentifier = hash
[crl_info]
URI.0 = $crl_url
[issuer_info]
caIssuers;URI.0 = $aia_url
OCSP;URI.0 = $ocsp_url
[name_constraints]
permitted;DNS.0=example.com
permitted;DNS.1=example.org
excluded;IP.0=0.0.0.0/0.0.0.0
excluded;IP.1=0:0:0:0:0:0:0:0/0:0:0:0:0:0:0:0
The fifth and final part of the configuration specifies the extensions to be used with the certificate for OCSP response signing. In order to be able to run an OCSP responder, we generate a special certificate and delegate the OCSP signing capability to it. This certificate is not a CA, which you can see from the extensions:
[ocsp_ext]
authorityKeyIdentifier = keyid:always
basicConstraints = critical,CA:false
extendedKeyUsage = OCSPSigning
keyUsage = critical,digitalSignature
subjectKeyIdentifier = hash
1.5.2.2 Root CA Directory Structure
The next step is to create the directory structure specified in the previous section and initialize some of the files that will be used during the CA operation:
$ mkdir root-ca
$ cd root-ca
$ mkdir certs db private
$ chmod 700 private
$ touch db/index
$ openssl rand -hex 16 > db/serial
$ echo 1001 > db/crlnumber
The following subdirectories are used:
-
certs/
-
Certificate storage; new certificates will be placed here as they are issued.
-
db/
-
This directory is used for the certificate database (index) and the files that hold the next certificate and CRL serial numbers. OpenSSL will create some additional files as needed.
-
private/
-
This directory will store the private keys, one for the CA and the other for the OCSP responder. It’s important that no other user has access to it. (In fact, if you’re going to be serious about the CA, the machine on which the root material is stored should have only a minimal number of user accounts.)
When creating a new CA certificate, it’s important to initialize the certificate serial numbers with a random number generator, as I do in this section. This is very useful if you ever end up creating and deploying multiple CA certificates with the same distinguished name (common if you make a mistake and need to start over); conflicts will be avoided, because the certificates will have different serial numbers.
1.5.2.3 Root CA Generation
We take two steps to create the root CA. First, we generate the key and the CSR. All the necessary information will be picked up from the configuration file when we use the -config
switch:
$ openssl req -new \
-config root-ca.conf \
-out root-ca.csr \
-keyout private/root-ca.key
In the second step, we create a self-signed certificate. The -extensions
switch points to the ca_ext
section in the configuration file, which activates the extensions that are appropriate for a root CA:
$ openssl ca -selfsign \
-config root-ca.conf \
-in root-ca.csr \
-out root-ca.crt \
-extensions ca_ext
1.5.2.4 Structure of the Database File
The database in db/index
is a plaintext file that contains certificate information, one certificate per line. Immediately after the root CA creation, it should contain only one line:
V 240706115345Z 1001 unknown /C=GB/O=Example/CN=Root CA
Each line contains six values separated by tabs:
-
Status flag (
V
for valid,R
for revoked,E
for expired) -
Expiration date (in
YYMMDDHHMMSSZ
format) -
Revocation date or empty if not revoked
-
Serial number (hexadecimal)
-
File location or
unknown
if not known -
Distinguished name
1.5.2.5 Root CA Operations
To generate a CRL from the new CA, use the -gencrl
switch of the ca
command:
$ openssl ca -gencrl \
-config root-ca.conf \
-out root-ca.crl
To issue a certificate, invoke the ca
command with the desired parameters. It’s important that the -extensions
switch points to the correct section in the configuration file (e.g., you don’t want to create another root CA).
$ openssl ca \
-config root-ca.conf \
-in sub-ca.csr \
-out sub-ca.crt \
-extensions sub_ca_ext
To revoke a certificate, use the -revoke
switch of the ca
command; you’ll need to have a copy of the certificate you wish to revoke. Because all certificates are stored in the certs/
directory, you only need to know the serial number. If you have a distinguished name, you can look for the serial number in the database.
Choose the correct reason for the value in the -crl_reason
switch. The value can be one of the following: unspecified
, keyCompromise
, CACompromise
, affiliationChanged
, superseded
, cessationOfOperation
, certificateHold,
and removeFromCRL
.
$ openssl ca \
-config root-ca.conf \
-revoke certs/1002.pem \
-crl_reason keyCompromise
1.5.2.6 Create a Certificate for OCSP Signing
First, we create a key and CSR for the OCSP responder. These two operations are done as for any non-CA certificate, which is why we don’t specify a configuration file:
$ openssl req -new \
-newkey rsa:2048 \
-subj "/C=GB/O=Example/CN=OCSP Root Responder" \
-keyout private/root-ocsp.key \
-out root-ocsp.csr
Second, use the root CA to issue a certificate. The value of the -extensions
switch specifies ocsp_ext
, which ensures that extensions appropriate for OCSP signing are set. I reduced the lifetime of the new certificate to 365 days (from the default of 3,650). Because these OCSP certificates don’t contain revocation information, they can’t be revoked. For that reason, you want to keep the lifetime as short as possible. A good choice is 30 days, provided you are prepared to generate a fresh certificate that often:
$ openssl ca \
-config root-ca.conf \
-in root-ocsp.csr \
-out root-ocsp.crt \
-extensions ocsp_ext \
-days 30
Now you have everything ready to start the OCSP responder. For testing, you can do it from the same machine on which the root CA resides. However, for production you must move the OCSP responder key and certificate elsewhere:
$ openssl ocsp \
-port 9080
-index db/index \
-rsigner root-ocsp.crt \
-rkey private/root-ocsp.key \
-CA root-ca.crt \
-text
You can test the operation of the OCSP responder using the following command line:
$ openssl ocsp \
-issuer root-ca.crt \
-CAfile root-ca.crt \
-cert root-ocsp.crt \
-url http://127.0.0.1:9080
In the output, verify OK
means that the signatures were correctly verified, and good
means that the certificate hasn’t been revoked.
Response verify OK
root-ocsp.crt: good
This Update: Jul 9 18:45:34 2014 GMT