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.
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
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
serverAuth, which is TLS client and server authentication. Second, the
nameConstraints extension limits the allowed hostnames only to
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
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:
Certificate storage; new certificates will be placed here as they are issued.
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.
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.
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
$ 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
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 (
Expiration date (in
Revocation date or empty if not revoked
Serial number (hexadecimal)
File location or
unknownif not known
To generate a CRL from the new CA, use the
-gencrl switch of the
$ 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:
$ openssl ca \ -config root-ca.conf \ -revoke certs/1002.pem \ -crl_reason keyCompromise
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