The Serpent

// Cursing the Internet since 1998

Setting up a Certification Authority using OpenSSL

Setting up a Certification Authority using OpenSSL

Ever tried running your own Certification Authority (CA)? With OpenSSL – it’s not as complicated as you think, though thanks to the lack of decent documentation, there are a few pitfalls. This article walks you through the basics of setting up OpenSSL to run a CA that can sign other end entity certificates.

At the time of writing, OpenSSL version 1.0.2a is the latest. It’s highly recommended as it contains the latest bug fixes, and creates SHA-2 certificates by default. This article assumes you have a working copy of OpenSSL.

Set up your environment

Working with OpenSSL generates a lot of files. It’s wise to set up a directory structure for your PKI environment. Let’s assume our new PKI service is for a company called ‘Starlight’. We’ll create the following:

/starlight-ca
  |-certs/
  |-issued-certs/
  |-private/
  |-serial
  |-index.txt
  |-rand
  |-crlnumber

All files can be empty, except ‘serial’. This needs a value – any value to start the serial numbers off from. Edit this file and enter 01.

To use this directory structure, we’ll now edit the OpenSSL configuration file. By default it lives in either the same directory as the executable (Windows), or within /etc/ssl/openssl.cnf on Linux. You can tell which configuration file OpenSSL loaded by running the ca command from within OpenSSL.

Note
Debian places the openssl.cnf file within /usr/lib/ssl/openssl.cnf

The configuration file is split into sections. Some sections are called automatically depending on the command being ran, others can be defined manually to perform custom functions, something which you’ll almost certainly require if you wish to make a modern V3 certificate. Before going further, let’s take a look over the OpenSSL commands and get the configuration file set up correctly.

OpenSSL Commands

It’s worth writing a solid configuration file to help make the commands easier as they can get quite convoluted. Before we look at the relevant sections, let’s remind ourselves of the various commands within OpenSSL that we’ll be using:

genrsa
Used to create private keys. In this example we’re using 2048 bit RSA keys, though there are commands to create DSA or even Elliptic Curve keys.

req
This one can get a little tricky as it performs multiple tasks. It’s primarily used to create certificate signing requests from a private key. But it can also be used to create self signed certificates with the help of the x509 command (provided as a switch to req).

ca
This command creates certificates by signing them against your defined Certification Authority.

x509
Not used directly (we call it using req), but allows us to create our root certificate. Can also be used to query certificates.

The Configuration File

Now we’re familiar with the commands, let’s see how they related to the configuration file. Here’s a quick breakdown of the default sections and their uses:

Section NamePurposeAssociated Command
[ ca ]Used by the ca command to determine which certification authority should sign requests.ca
[ CA_default ]The default Root CA. It's referenced by the above, allowing you to have multiple CA's defined in a config file. But we'll just use the one for our testing.ca
[ req ]Evaluated when you issue req commandsreq, x509
[ usr_cert ]Called from [ CA_Default ] via the x509_extensions to add additional V3 non-CA extensions to certificates.ca
[ v3_req ]Not actually referenced in the default configuration file. Replaced by [ v3_ca ].n/a
[ v3_ca ]Called by req. Optional V3 extensions to add to a signing request.req

Part of the problem with these default sections is that they don’t accurately reflect the purpose of the section. That is why we’re going to start from scratch and name our sections something relevant. Lets create a very simple configuration file containing the bare minimum to run a CA. We’ll go through it piece by piece for simplicity. The first section contains:

# Starlight custom OpenSSL conf
# aimed at 1.0.2a which generates SHA256 certs by default it would seem.

HOME		= .
RANDFILE	= $ENV::HOME/.rnd

####################################################################
[ ca ]
default_ca	= CA_default		# The default ca section

####################################################################

We start with a couple of global variables that handle the randomisation file and give us as working directory, then we move into the first main section, the [ ca ] section specifies the settings for our ca command, and will detail the certificate\key we should sign requests against by default.

It points to CA_Default so let’s define that:

[ CA_default ]

dir		= /home/jo/starlight-ca	# Where everything is kept
certs		= $dir/certs		# Where the issued certs are kept
crl_dir		= $dir/crl		# Where the issued crl are kept
database	= $dir/db.txt		# database index file.
#unique_subject	= no			# Set to 'no' to allow creation of
					# several ctificates with same subject.
new_certs_dir	= $dir/issued-certs	# default place for new certs.

certificate	= $dir/starlight-ca.crt # The CA certificate - Change to the name of your Root CA Cert
serial		= $dir/serial 		# The current serial number
crlnumber	= $dir/crlnumber	# the current crl number
					# must be commented out to leave a V1 CRL
crl		= $dir/crl.pem 		# The current CRL
private_key	= $dir/starlight-ca.key # The private key - Change to the name of your Root CA key
RANDFILE	= $dir/rand		# private random number file

x509_extensions = v3_cert		# make ca signing requests v3 certs

# Comment out the following two lines for the "traditional"
# (and highly broken) format.
name_opt 	= ca_default		# Subject Name options
cert_opt 	= ca_default		# Certificate field options

# Extensions to add to a CRL. Note: Netscape communicator chokes on V2 CRLs
# so this is commented out by default to leave a V1 CRL.
# crlnumber must also be commented out to leave a V1 CRL.
# crl_extensions	= crl_ext

default_days	= 365			# how long to certify for
default_crl_days= 30			# how long before next CRL
default_md	= default		# use public key default MD
preserve	= no			# keep passed DN ordering

# A few difference way of specifying how similar the request should look
# For type CA, the listed attributes must be the same, and the optional
# and supplied fields are just that :-)
policy		= policy_match

# For the CA policy
[ policy_match ]
countryName		= match
stateOrProvinceName	= match
organizationName	= match
organizationalUnitName	= optional
commonName		= supplied
emailAddress		= optional

# For the 'anything' policy
# At this point in time, you must list all acceptable 'object'
# types.
[ policy_anything ]
countryName		= optional
stateOrProvinceName	= optional
localityName		= optional
organizationName	= optional
organizationalUnitName	= optional
commonName		= supplied
emailAddress		= optional

####################################################################

There’s quite a bit here, but the important parts are the very top variables, where we define the location of our newly created certificates and root CA key. Don’t worry if these don’t exist yet, since when generating a self signed root CA – we don’t actually use this section.

Another important line is x509_extensions = v3_cert, this defines what extensions we’ll be adding to any newly created certificate. We’d like nice new V3 certificates, so we’ll define a section named v3_cert later on.

Now it’s time to create our Root certificate. We’ll need a little more configuration first though, as we’ll be using the req command, we’ll need a [ req ] section, so let’s place that in:

[ req ]
default_bits		= 2048
default_keyfile 	= private.pem
distinguished_name	= req_distinguished_name
string_mask 		= utf8only
x509_extensions		= v3_ca


[ req_distinguished_name ]
countryName			= Country Name (2 letter code)
countryName_default		= GB
countryName_min			= 2
countryName_max			= 2

stateOrProvinceName		= State or Province Name (full name)
stateOrProvinceName_default	= MySate

localityName			= Locality Name (eg, city)

0.organizationName		= Organization Name (eg, company)
0.organizationName_default	= Starlight

organizationalUnitName		= Organizational Unit Name (eg, section)
organizationalUnitName_default	= IT Services

commonName			= Common Name (e.g. server FQDN or YOUR name)
commonName_max			= 64

emailAddress			= Email Address
emailAddress_max		= 64

This is a good section to edit so you can change the default values of your certificate signing requests, but again the important line is x509_extensions = v3_ca, which tells OpenSSL what section to use when we want to use req to actually sign a certificate (instead of just giving us a certificate signing request).

Note
The `req` command has two functions, it can generate certificate signing requests, which then get fed into the `ca` command, or it can generate self signed certificates, using the `-x509` switch.

Finally, let’s create the [ v3_ca ] section to define how we’d like our CA’s to look:

[ v3_ca ]
# This section is called from [ req ] when -x509 is used. It tells req to 
# produce a CA rather then a CSR.

# Note this is more of a one time setup for our initial CA, then shouldn't really 
# be used again unless creating equally strong sub CA's 

basicConstraints 	= 	CA:TRUE
subjectKeyIdentifier 	= 	hash
authorityKeyIdentifier	=	keyid,issuer
extendedKeyUsage 	= 	serverAuth,clientAuth,emailProtection
keyUsage 		= 	digitalSignature,cRLSign,keyCertSign

####################################################################

This section has only one purpose – to create a CA certificate. The extensions we add will provide the normal values for a V3 CA certificate.

Creating the Root CA

Once all these sections have been saved into the openssl.cnf file, we can create our root CA using the following command:

req -x509 -newkey rsa:2048 -keyout starlight-ca.key -out starlight-ca.crt

This command calls req, which in turn also calls x509 to generate a root CA. It creates a private key for us also (you could do this step separately if you wanted, look into the genrsa command). Once complete, you’ll get a root CA certificate that is capable of signing other certificates. To summarise:

  1. The `req` command looked for the `[ req ]` section in the configuration file and read the defaults
  2. Because we specified `-x509`, the configuration checked for the existence of the `x509_extensions` directive, and executed the section (in this case, `v3_ca`)
  3. This section has the basicContraints set to provide a CA, so the result is a V3 CA Certificate.

Note that the output file names match the values in the CA_Default section. Ensure that these files are within the directory specified in that section and you can now use the [ CA_Default ] section to sign certs against your root without having to specify which Root Certificate and key to sign against every time.

Before we start signing certificates, there’s one more section that needs to be created. Remember that our default section contains x509_extensions = v3_cert? We’ll need to define this, as it tells OpenSSL exactly how to create a standard (non-CA) V3 certificate:

[ v3_cert ]
# Called when using the 'ca' command, referenced in the Default_CA section 
# to create standard end entity server certs

basicConstraints 	= 	CA:FALSE
subjectKeyIdentifier 	= 	hash
authorityKeyIdentifier	=	keyid,issuer
extendedKeyUsage 	= 	serverAuth
keyUsage		=	digitalSignature

####################################################################

Don’t forget to restart OpenSSL before continuing, as it wont see the changes to the configuration file until you do.

Creating Signed Certificates

Now we can execute the command:

genrsa -out server.key 2048 This gives us a 2048 bit RSA private key. Now we’ll use it to generate a certificate signing request:

req -new -key server.key -out server.csr You’ll now have a nice new ceritifcate signing request. The last step is to feed that request into the ca command to produce a signed certificate. Assuming the variables within your CA_Default section point to your newly created certificate and private key, you can execute:

ca -out server.crt -infiles server.csr The result will be a V3 end entity server certificate that can be used to secure any HTTPS web server! Install the root CA into your trusted authority store and you should see a valid chain between the two.

Creating a Client Certificate

The same process can be used to create client certificates, you’ll need to change the extensions slightly, so it’s easier to create a new dedicated section within the configuration file:

[ v3_client_cert ]
# Client cert creation

basicConstraints	=	CA:FALSE
nsCertType		=	client, email
extendedKeyUsage 	= 	clientAuth
keyUsage		=	digitalSignature

####################################################################

Now we simply generate a private key exactly as before, sign it in the same way, but reference this new section within the ca command to override the default section of v3_cert:

genrsa -out gfreeman.key 2048 req -new -key gfreeman.key -out gfreeman.csr ca -out gfreeman.crt -extensions v3_client_cert -infiles gfreeman.csr

OpenSSL isn’t the easiest software to get along with, this information is correct to the best of my knowledge (and for lack of any other decent documentation), but hopefully it should help you understand the difficulties of dealing with the configuration file sections. As with many OpenSSL tasks, there are also more than one way to execute the commands (and you can shorten the steps further, such as generating private keys and CSR’s together using req, hint: look up -newkey), but breaking them into their individual steps makes it much easier if you’re just starting out.