michael orlitzky

New certificates in IIS without downtime

posted 2010-03-16

This article is a follow-up to Replacing a Certificate in IIS without Downtime; I realized that it is probably non-obvious how one would acquire the signed certificate in the first place.

The Short Version

You can go read the intro to the first article for the long explanation. In short, IIS makes it completely impossible to generate a new certificate request for a site without taking that site offline. So we're going to cheat and use OpenSSL.

OpenSSL comes with just about every Linux distribution, so if you have a Linux machine handy, you can use it to perform all of these steps. Otherwise, go get Cygwin and install it along with its OpenSSL package.

Generating the Signing Request

Since we're going to use a new certificate/key, we need to generate a new CSR. OpenSSL can do this, but it's ugly. I created a shell script, vhost-ssl-request, to do it:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
#!/bin/bash
#
# vhost-ssl-request
#
# Generate a certificate signing request (CSR) for a virtual host.
#

# How many bits to use for the key?
NUM_BITS=2048

function usage() {
    echo "Usage: $0 [-b NUM_BITS] <vhost>"
    echo ''
    echo -n '  <vhost>        '
    echo 'Your virtual hostname, i.e. the common name on the certificate.'
    echo ''
    echo -n '  -b NUM_BITS    '
    echo "Create keys of length NUM_BITS (default ${NUM_BITS})."
    echo ''
}

# Some exit codes.
EXIT_BAD_ARGS=1
EXIT_KEY_EXISTS=2
EXIT_CSR_EXISTS=3

while getopts "b:" option; do
    case $option in
	b ) NUM_BITS=$OPTARG;;
	* ) usage
	    exit $EXIT_BAD_ARGS;;
    esac
done

# Get rid of the -b option if it was passed.
shift $((OPTIND-1))

if [ $# -lt 1 ]; then
    usage
    exit $EXIT_BAD_ARGS
fi

VHOST=$1

KEYFILE=${VHOST}.key
TODAY=$(date +"%Y-%m-%d")
CSRFILE=${VHOST}-${TODAY}.csr

# Make sure the key/CSR don't already exist before we proceed.
if [ -f $KEYFILE ]; then
    echo "Key file $KEYFILE already exists. Bailing."
    exit $EXIT_KEY_EXISTS
fi

if [ -f $CSRFILE ]; then
    echo "CSR file $CSRFILE already exists. Bailing."
    exit $EXIT_CSR_EXISTS
fi

# Generate the private key.
openssl genrsa -out $KEYFILE $NUM_BITS

# The private key is private! Make it read-only.
chmod 400 $KEYFILE

# Generate the signing request.
# Always uses SHA-2, since SHA-1 is on its way out:
#
# https://googleonlinesecurity.blogspot.com/2014/09/gradually-sunsetting-sha-1.html
#
openssl req -new -sha256 -key $KEYFILE -out $CSRFILE

Note the default NUM_BITS=4096. Change it if you care. Run the thing, and pass it the name of your site (i.e. the certificate's common name).

user $ ./vhost-ssl-request www.example.com

This will output two files in the current directory, www.example.com.key, your private key, and www.example.com-<date>.csr, the certificate signing request.

Obtaining the Certificate

You've got the CSR; just send it off to whomever is going to sign it. Generally, once they have verified that you own the common name in question, you will receive a certificate file back. Let's call it www.example.com.crt.

Now, IIS won't import a certificate/key pair as separate files, because it's a fucker. So before we import the pair, we need to convert it to PKCS12 format (a pfx file). OpenSSL can do this, but first, let's combine the certificate/key pair into one pem file.

user $ cat www.example.com.key www.example.com.crt > www.example.com.pem

Now, convert the pem file to pfx, with which IIS and Windows are in love:

user $ openssl pkcs12 -in www.example.com.pem -export -out www.example.com.pfx

You will be prompted for a password. Enter something short and memorable—we're going to use it once in about 10 seconds, and then never need it again.

We're almost done. The rest of the steps follow the previous article, beginning at Importing the New Certificate/Key Pair.