ben vandgrift · on living the dream

Secure Communication for Rails Applications (2)

Creating a Certificate Authority

This is the second in a series dealing with securing the environment of a rails environment. If you missed the introduction, you might want to give it a read.

In this segment, we'll create our own certificate authority (CA) to sign and verify clients connecting to our application, ensuring the clients are who they say they are. Whether you want to do things the easy way or the hard way depends on how much control you want over they way your CA behaves.

The Easy Way: CA.pl

CA.pl is a script bundled with OpenSSL to simplify the most common cert creation and signing tasks. You can use this to do the heavy lifting when creating a CA:

/path/to/openssl/CA.pl -newca

This creates a root certification authority certificate, which you can use to sign and verify other certs, and a reasonable directory structure (demoCA/) for keeping track of things.

CA.pl assumes that either you're creating a new CA, or that the demoCA/ directory exists and has been properly populated. It also contains a number of defaults that might be worth customizing; new certs are created with a 1-year expiration and the root certification authority certificate has a 3-year expiration, for instance.

When running the script, you'll be asked to enter a pass phrase. This should be something reasonable but hard to guess since you'll be entering it every time you sign a certificate request. The rest of the fields should be easy enough to figure out, but be sure and enter a Common Name.

Creating Certs with CA.pl

After your CA is set up, you're going to want to create three certs: one for the application/web server, one for our typical user, and one for an administrative user. This also can be done with CA.pl:

/path/to/openssl/CA.pl -newreq
/path/to/openssl/CA.pl -signreq

This will create three files in the current directory:

For the typical and administrative users, we go the extra step of creating PKCS12 files for them before renaming them, since we're likely to be installing the certs into a browser. Once again, this is handled by CA.pl:

/path/to/openssl/CA.pl -pkcs12

You'll be asked for an export password, to be tied to the PKCS12 file. This will generate newcert.p12.

The .pem files can be renamed whatever you like with any extension (server.csr, server.key, and server.crt, for example). The .p12 should keep its file extension.

At this point, you should have a server keypair, as well as a keypair and p12 file for both user and admin.

The Hard Way: Customizing CA.pl and openssl.cnf

If you're satisfied with what we have, feel free to move on. If you want to include your CA db in a source repo (see note below) or change some policies, you might want to do some tweaking.

When you look through CA.pl, you'll see that it's simply a wrapper around a number of defaults passed into openssl. In fact, the CA.pl source is a wonderful–if terse–introduction to the most commonly used openssl commands.

For this project, create the certs/ directory into which all the credentials will be collected, and certs/ca to hold the CA database. Change the default names cacert.pem and cakey.pem to retsyn-ca.pem and retsyn-ca.key, respectively. I'd also like to customize CA.pl a little bit, and put it in a bin directory to keep the project neat.

Finally, I don't like the defaults set up in the system's openssl.cnf file, so I'll instruct CA.pl to use a different configuration.

Back to the shell:

mkdir -p bin certs
mv demoCA certs/ca
cp /path/to/openssl/CA.pl bin
cp /path/to/openssl/config/openssl.cnf certs/ca

Editing CA.pl, we change the configuration section:

SSLEAY_CONFIG="-config ./certs/ca/openssl.cnf"
DAYS="-days 730";               # 2 years instead of 1
CADAYS="-days 3652";            # ~10 years instead of three
...
CATOP="./certs/ca";             # demoCA seems meh
CAKEY="retsyn-ca.key";          # root CA cert key file
CACERT="retsyn-ca.pem";         # root CA cert file

For openssl.cnf, the obvious changes are these:

dir         = ./certs/ca                 # Where everything is kept
certificate = $dir/retsyn-ca.pem         # The CA certificate
private_key = $dir/private/retsyn-ca.key # The private key

We can also change the way the config file behaves when creating certificates by defining policy requirements within the openssl.cnf:

[policy_custom]
countryName             = match
stateOrProvinceName     = match
organizationName        = match
organizationalUnitName  = optional
commonName              = supplied
emailAddress            = supplied

match indicates that the cert's value will be the same as the CA's, supplied values will be given by the generating user, optional values are not required.

There are also a number of [req] and [req_*] options that can be set for arbitrary levels of specificity.

Note: you shouldn't put your root CA certificate key or any other key file anywhere public, or anywhere it might be easily snagged. This includes public git repos and the like. (Yes, while all of my code for this article is on github, it's not intended to be used in any production environment, it's just for demonstration. Shut up.) Conversely, you MUST put your root CA certificate somewhere public if you want your users to be able to trust your root CA. Users who don't want to get a nasty warning message about your shady tactics will need to install your root CA into their browser or environment.

The Really Hard Way: Straight to openssl

If you really want explicit control over every aspect of this process you can issue openssl commands directly; in the words of @altonbrown, that's another show.

Next Time

In the next post, we'll build our authentication system.

written: May 13 2011