random memes }

Example of certificate creation with OpenSSL

If you need to create certificates for a test setup, the online examples and O'Reilly OpenSSL book examples - while instructive - seem rather awkward and obscure. The fault lies with OpenSSL (otherwise a great piece of software) and the mash of parameters between the configuration file, the command line, and interactive entry. The mash makes OpenSSL usage obscure, so you get a lot of examples of cargo-cult programming in the public examples.

I hate cargo-cult programming.

When a command requires a complex set of parameters, I prefer to pull the parameters from a file. Parameter files can be generated by script, archived, and/or stored in source control. With OpenSSL you cannot (quite) do that. Even worse, the default configuration file mashes together parameters from distinct commands in a non-obvious way. Thus folk looking at OpenSSL usage are often uncertain about what is used and when ... and you get "I hacked this until it worked, but not sure how" examples.

Since I have to feed parameters to OpenSSL both in the configuration file and on the command line, I chose to use shell scripts, create the configuration files from script when needed, and run the command with any remaining needed parameters.

The example is a Mercurial repository at:

http://hg.bannister.us/public/ca

Or just clone the set via:

hg clone hg.bannister.us:/public/ca

All the parameters for CA creation are in do/ca-create.sh

test -d root || mkdir root || exit 1 test -d history || mkdir history || exit 2

export password1='really cool password' export password2="$password1"

export config=history/ca-create.cnf

cat > $config << XXX

default_md = sha1 default_bits = 2048 x509_extensions = root_ca_certificate_extensions

# You will want to change this, when security matters. input_password = $password1 output_password = $password2

[ root_ca_certificate_extensions ]

subjectKeyIdentifier = hash keyUsage = digitalSignature,keyCertSign,cRLSign #authorityKeyIdentifier = keyid:always,issuer:always basicConstraints = critical,CA:true

[ req ]

prompt = no distinguished_name = root_ca_distinguished_name

[ root_ca_distinguished_name ]

# You will want to change this, to match your target server. # The order of these attributes is significant. countryName = US #stateOrProvinceName = California organizationName = Bannister @ Home organizationalUnitName = Root Certification Authority commonName = bannister.home CA emailAddress = preston@bannister.us

XXX

openssl req -config $config -x509 -new -days 365 -keyout root/ca-private-key.pem -out root/ca-certificate.pem

The bits above that you will want to change are in italics. To create your CA run:

sh do/ca-create.sh

To get a certificate for a named entity, first you have to generate a certificate request. For public web servers, the certificate request is what you send to one of the CA roots (Verisign, etc.) along with money, to get your certificate signed. For test setups, better to sign your own certificates. (Large organizations - like the DoD - also sign their own certificates.) All the parameters for creating certificate requests, with the exception of the entity name, are in the file do/request.sh:

test -z "$1" && { echo 'Usage: request-certificate name' exit 1 } test -d history || mkdir history || exit 2

export name=$1 echo name = $name

export password1='really cool password' export password2="$password1"

export config=history/request-$name.cnf export base="cn-$name"

test -d requests || mkdir requests || exit 1 test -d $base || mkdir $base || exit 2

cat > $config << XXX

default_md = sha1 default_bits = 2048 x509_extensions = root_ca_issued_certificate_extensions

# You will want to change this, when security matters. input_password = $password1 output_password = $password2

[ root_ca_issued_certificate_extensions ]

basicConstraints = CA:false

# PKIX recommendations. subjectKeyIdentifier = hash authorityKeyIdentifier = keyid,issuer:always

# Typical in keyUsage for a client certificate. # keyUsage = nonRepudiation, digitalSignature, keyEncipherment

[ req ]

prompt = no distinguished_name = request_distinguished_name

[ request_distinguished_name ]

# You will want to change this, to match your target server. # The order of these attributes is significant. countryName = US #stateOrProvinceName = California organizationName = Bannister @ Home organizationalUnitName = Testing commonName = $name emailAddress = preston@bannister.us

XXX

openssl req -config $config -new -keyout $base/private-key.pem -nodes -out requests/$name.pem

Again, the part you will want to change is in italics. For example: to generate a certificate request to server named "foozle" you would run the command:

sh do/request.sh foozle

Repeat the command for all your named entities.

Once you have created all certificate requests, have your CA sign all the certificates. All the parameters for processing certificate requests are in the file do/requests.sh:

test -d root/db || { mkdir root/db || exit 1 touch root/db/index.txt echo 01 > root/db/serial } test -d history || mkdir history || exit 2 export issued=root/issued test -d $issued || mkdir $issued || exit 3

export password1='really cool password' export password2="$password1"

export config=history/ca.cnf

cat > $config << XXX

default_md = sha1

# You will want to change this, when security matters. input_password = $password1 output_password = $password2

[ ca ]

default_ca = root_ca

[ root_ca ]

certs = root/certificates new_certs_dir = $issued crl_dir = root/crl

database = root/db/index.txt serial = root/db/serial crlnumber = root/db/crlnumber

certificate = root/ca-certificate.pem private_key = root/ca-private-key.pem crl = root/crl.pem

default_days = 365 default_crl_days = 30 unique_subject = no #preserve = no

policy = root_ca_policy x509_extensions = root_ca_issued_certificate_extensions

[ root_ca_policy ]

countryName = supplied stateOrProvinceName = optional organizationName = supplied organizationalUnitName = optional commonName = supplied emailAddress = supplied

[ root_ca_issued_certificate_extensions ]

basicConstraints = CA:false

XXX

for request in ls requests ; do echo "===== Processing request: $request" export out="cn-basename $request .pem/certificate.pem" if openssl ca -config $config -batch -in requests/$request -key "$password1" > $out ; then mv requests/$request history/. echo "..... OK - Generated: $out" else rm $out echo "\n***** ERROR in processing request: $request\n" fi done

Likely you do not to make any changes to the above. To process all the pending certificate requests, run:

sh do/requests.sh

The private key and public certificate for each named entity end up in the cn-* directories. The cn-foozle directory would contain the private key and public certificate for the "foozle" named entity.

The Makefile is meant as an example of creating the CA and certificates for all named entities in one operation.

# # Example makefile for creating a CA and certificates for named entities. #

all : # default rule

NAMED= m2003 m2008 z2003 z2008 john sammy alice

clean : clean-named ; @rm -rf root history requests clean-named : ; @rm -rf cn-*

root : clean ; sh do/ca-create.sh all : root ; for name in $(NAMED) ; do sh do/request.sh $$name ; done ; sh do/requests.sh

.PHONY : all clean clean-named

Nothing complicated here. Run make to create the CA and certificates for all named entities. Run make clean start over from a clean state. Note that the configuration files fed to OpenSSL are saved in the history/ directory. (Did I mention that I like to be thorough?)