Custom Certificate Authorities
Setting up your own certificate authority
Note: this page was written in 2008. It is still current, but there might subtle changes in tool invocation. The default key size has been increased to 2048 bits (was: 1024) and the default signing algorithm is now sha-256 (was: md5)
Sometimes I need to play around with some digital certificates and I do not feel like shelling out a lot of money each time to buy real ones. Here’s how to set up your own CA (certificate authority) in a quick-and-dirty way.
The scenario in which I am interested is to set up a single root-CA, which signs the certificates of two sub-authorities. The sub-authorities are the entities that actually sign the end-user certificates.
Create some directories and set up some initial files:
$ mkdir -p ca/root-ca/{private,certs} ca/person-ca/{private,certs} ca/site-ca/{private,certs}
$ touch ca/{root,site,person}-ca/certindex.txt
$ for i in ca/{root,site,person}-ca; do echo '100001' > $i/serial;done Generate the root certificate:
$ cd ca/root-ca
Create a new file called openssl.cnf
with the following content:
#
# OpenSSL configuration file.
#
# Establish working directory.
dir = .
[ ca ]
default_ca = CA_default
[ CA_default ]
serial = $dir/serial
database = $dir/certindex.txt
new_certs_dir = $dir/certs
certificate = $dir/cacert.pem
private_key = $dir/private/cakey.pem
default_days = 365
default_md = md5
preserve = no
email_in_dn = no
nameopt = default_ca
certopt = default_ca
policy = policy_match
[ policy_match ]
countryName = match
stateOrProvinceName = match
organizationName = match
organizationalUnitName = optional
commonName = supplied
emailAddress = optional
[ req ]
default_bits = 2048 # Size of keys
default_keyfile = key.pem # name of generated keys
default_md = sha256 # message digest algorithm
string_mask = nombstr # permitted characters
distinguished_name = req_distinguished_name
req_extensions = v3_req
[ req_distinguished_name ]
# Variable name Prompt string
#------------------------- ----------------------------------
0.organizationName = Organization Name (company)
organizationalUnitName = Organizational Unit Name (department, division)
emailAddress = Email Address
emailAddress_max = 40
localityName = Locality Name (city, district)
stateOrProvinceName = State or Province Name (full name)
countryName = Country Name (2 letter code)
countryName_min = 2
countryName_max = 2
commonName = Common Name (hostname, IP, or your name)
commonName_max = 64
# Default values for the above, for consistency and less typing.
# Variable name Value
#------------------------ ------------------------------
0.organizationName_default = Your Corp
localityName_default = Your Town
stateOrProvinceName_default = Your State
countryName_default = US
[ v3_ca ]
basicConstraints = CA:TRUE
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid:always,issuer:always
[ v3_req ]
basicConstraints = CA:FALSE
subjectKeyIdentifier = hash
Generate a new root CA certificate:
$ openssl req -new -x509 -out root-ca-crt.pem -newkey rsa:2048 \
-keyout private/root-ca-key.pem -days 365 -extensions v3_ca \
-config openssl.cnf
The first sub-authority is a little more work.
$ cd ../site-ca
$ openssl req -new -config ../root-ca/openssl.cnf -newkey rsa:2048 \
-keyout private/site-ca-key.pem -days 365 -extensions v3_ca \
-out site-ca-req.pem
Sign the new certificate with the root cert:
$ cd ../root-ca
$ openssl ca -config openssl.cnf -days 365 -extensions v3_ca \
-keyfile private/root-ca-key.pem \
-cert root-ca-crt.pem \
-out ../site-ca/site-ca-crt.pem -in ../site-ca/site-ca-req.pem
Repeat for the person CA:
$ cd ../person-ca
$ openssl req -new -config ../root-ca/openssl.cnf -newkey rsa:2048 \
-keyout private/person-ca-key.pem -days 365 -extensions v3_ca \
-out person-ca-req.pem
Sign the new certificate with the root cert:
$ cd ../root-ca
$ openssl ca -config openssl.cnf -days 365 -extensions v3_ca \
-keyfile private/root-ca-key.pem \
-cert root-ca-crt.pem \
-out ../person-ca/person-ca-crt.pem -in ../person-ca/person-ca-req.pem
Generate a new certificate signing request for a person certificate:
$ cd ../person-ca
$ mkdir csr
$ openssl req -config ../root-ca/openssl.cnf -newkey rsa:2048 \
-keyout private/kees.leune-key.pem -out csr/kees.leune-req.pem \
-days 365 -nodes
This generates a signing request without passphrase protection. To be
prompted for a phrase, omit the -nodes
switch.
Sign the request with the person-ca key:
$ openssl ca -config ../root-ca/openssl.cnf -days 365 \
-keyfile private/person-ca-key.pem \
-cert person-ca-crt.pem \
-in csr/kees.leune-req.pem \
-out csr/kees.leune-crt.pem
Repeat for a site and generate a new certificate signing request for a person certificate:
$ cd ../site-ca
$ mkdir csr
$ openssl req -config ../root-ca/openssl.cnf -newkey rsa:2048 \ -keyout private/site-key.pem -out csr/site-req.pem \ -days 365 -nodes
This generates a signing request without passphrase protection. To be prompted for a phrase, omit the -nodes switch.
Sign the request with the site-ca key:
$ openssl ca -config ../root-ca/openssl.cnf -days 365 \ -keyfile private/site-ca-key.pem \ -cert site-ca-crt.pem \ -in csr/site-req.pem \ -out csr/site-crt.pem
Copy root-ca-crt.pem
, site-ca-crt.pem
, and person-ca-crt.pem
to a
directory and run c_rehash .
in that directory. In an Apache mod_ssl
configuration, point the SSLCACertificatePath
to the directory with the
certificates. Copy the files site-crt.pem
and site-key.pem
to a place
where the web server can read them and point the SSLCertificateFile
and
SSLCertificateKeyFile
to them. This will lead to an Apache Virtual Host definition like:
SSLEngine On
SSLCertificateFile /etc/apache2/ssl/airt-dev-vm.crt
SSLCertificateKeyFile /etc/apache2/ssl/airt-dev-vm.key
SSLCACertificatePath /etc/apache2/ssl
ServerAdmin webmaster@localhost
ServerName your-host-name
SSLVerifyDepth 3
SSLVerifyClient require
SSLOptions +ExportCertData
Make sure you install the same certificates that you installed in the Apache CA directory in your browser.
Lastly, generate a p12 certificate request. Make sure you are in the person CA directory:
$ openssl pkcs12 -export -in csr/kees.leune-crt.pem -inkey private/kees.leune-key.pem \ -certfile ../person-ca/person-ca-crt.pem \ -name "Kees Leune" \ -out csr/kees.leune-crt.p12