/usr/portage

Batch-generating SSL certificates 3

I had an itch to scratch this day: generate a huge amount of SSL-certificates for a set of whitelabels. Every whitelabel has it’s own domain and needs it’s own SSL certificate. I don’t want to spend n ⋅ x ¤ to buy them just for testing purpose (generating a huge number would have been an issue too if I bought them but …). So I needed a script to do it. We had one before to generate a single one with the typical user interactions. While hacking it down I found out a few neat things about OpenSSL which I didn’t know before and which I think are worth sharing.

Disclaimer: the script is not hacked together with security in mind. It simply doesn’t matter for me in this specific case.

So after alot of chit-chatting, there we go:

#!/bin/bash
DOMAIN="$1"
if [ -z "$DOMAIN" ]; then
    echo "Usage: $(basename $0) <domain>"
    exit 11
fi

Obvious: we need to call the script with a domain as a parameter. Next is a simple to fail when a certain condition matches:

fail_if_error() {
    [ $1 != 0 ] && {
        unset PASSPHRASE
        exit 10
    }
}
export PASSPHRASE=$(head -c 128 /dev/random  | uuencode - | grep -v "^end" | tr "\n" "d")

We generate a passphrase by reading 128 byte from /dev/random, encoding it with base64 and joining the lines. A simple trick if you can’t rely on pwgen or makepasswd. Next is to define the subject string. But we’ll talk about that later:

subj="
C=<Country Code>
ST=<State>
O=<Company>
localityName=<City>
commonName=$DOMAIN
organizationalUnitName=<Unit Name>
emailAddress=<email>
"

Of all things generating a fresh RSA key comes first. The neat trick to avoid user interaction here is to use -passout option. This option takes an protocol argument similar to PHPs stream wrappers. env:<envvar> for environment variables, pass:<password> if you like to pass the string directly, file:<filename> to read from a file, fd:<number> to read from a file descriptor and stdin – well – to read from the standard input. Of course we fail on error.

openssl genrsa -des3 -out $DOMAIN.key -passout env:PASSPHRASE 2048
fail_if_error $?

Now we generate a certificate request (.csr). -batch simply supresses user interaction. And here comes the $subj variable into play which allows us to override our certificate requests defaults specified in our openssl.conf. Most of the time the OpenSSL configuration file should be located in /etc/ssl/openssl.cnf but if you are on Mac OS X it is located in /System/Library/OpenSSL/openssl.cnf. The subject variables you can override are defined in this configuration. It’s an INI file, just search for the section [ req ] and look at the keys distinguished_name and req_attributes. There values redirect to the section name we are searching for: here you can find the candidates you might want to override in your subject string. Just extend the $subj variable according to your needs. The $subj variable lines are joined with a slash as a seperator – this is how OpenSSL likes it.

openssl req \
    -new \
    -batch \
    -subj "$(echo -n "$subj" | tr "\n" "/")" \
    -key $DOMAIN.key \
    -out $DOMAIN.csr \
    -passin env:PASSPHRASE
fail_if_error $?
cp $DOMAIN.key $DOMAIN.key.org
fail_if_error $?

Strip the passphrase from our RSA-key to not get prompted when Apache (or any other webserver) starts:

openssl rsa -in $DOMAIN.key.org -out $DOMAIN.key -passin env:PASSPHRASE
fail_if_error $?

Last step, create the certificate file:

openssl x509 -req -days 365 -in $DOMAIN.csr -signkey $DOMAIN.key -out $DOMAIN.crt
fail_if_error $?

Now: deploy the .key and .crt file to your webserver and set up your configuration.

Filed on 14-08-2009, 22:10 under , , , , , & three comments & no trackbacks