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?)