SSL CA and certificate basics

If you're not familiar with SSL certificates at all, read the SSL certificate basics page for background information. If you don't understand SSL certificates in general, none of the rest will make sense. The commands on this page assume that you're either working on a modern Unix machine or a Windows machine with the Cygwin environment installed, and have a current version of OpenSSL installed. I don't provide instructions for native Windows since the tools there tend to be proprietary, GUI-based and too difficult to provide written instructions for.

Creating a CA using RSA-based certificates

SSL certificates are too useful a thing to not be able to get them on demand. At the same time, for development you don't want to be spending money on them when they'll only be used internally. You can kludge things together by doing plain self-signed certificates, but that means importing every individual certificate into every piece of software that needs it. A better way is to create your own CA with a root certificate, then you can import that CA certificate into your software and issue any number of certificates as needed and have them accepted. Note that this won't be useful for public-facing servers, but it's handy for development systems where it'll just be you accessing them or for internal systems like e-mail servers where you know what machines should be accessing them and can install the CA certificate as needed without too much trouble. There's several pieces of software that'll help with that, but they gloss over what's happening and don't always work the way you'd like. I ended up working up the commands to do the job directly, it turns out they aren't that complicated, and I decided to document things here. This assumes you've got OpenSSL installed.

First, you'll need to decide where you're going to locate your CA. The ideal place is under a regular user account on a machine not routinely used for browsing the Web or connecting to outside sites. For real security you can put the entire CA on a flash drive and simply remove it from the machine when not in use. Wherever you decide, make sure that directory exists. We'll call it ${DIR} for convenience. Then create the following directories under it: private, certs, newcerts, crl. Set the permissions on private, newcerts and crl to "u=rwx,go=" (no access for anyone but the owner). The commands would be:

mkdir ${DIR}
cd ${DIR}
mkdir private
mkdir certs
mkdir newcerts
mkdir crl
chmod a=rwx,go= private newcerts crl

Then create the housekeeping files that'll be needed later:

echo 01 >crlnumber
echo 1000 >serial
touch index.txt

Now, locate your openssl.cnf file. Common locations for it are /etc/ssl, /usr/lib/ssl or /usr/ssl. You'll need to modify it slightly to reflect your soon-to-be CA. Find the "[ CA_default ]" section, and the first thing in it should be a "dir" setting. Change it's value to ${DIR}, so it's now pointing at the root of your CA directory tree. Down lower there should be a "default_days" entry. It's initially set to 365 (1 year). I prefer to set it to 730 (2 years) to avoid having to issue new certificates quite as often. Resist the temptation to set it to be much longer than 5 years. Issuing new server certificates is a lot less hassle when you've got your own CA root certificate and having certificates expire in a reasonable amount of time means you don't have to worry about compromised certificates forever. Further down in the "[ req ]" section you'll find a "default_bits" entry. I bump this up to 4096, because anything shorter is becoming too easy to brute-force with modern hardware.

Below this is the "[ req_distinguished_name ]" section. You may want to modify some of the "*_default" entries here to reflect the common values you'll be using. You don't need to, since you can enter non-default values for them when creating certificates, but if you're always going to be in one city it saves typing to have that city as the default value. The most common ones you'll want to do this for are "countryName", "stateOrPrivinceName" and "organizationName".

At this point you've got the basic files and OpenSSL set up. The first thing you want to do is create the key and certificate for your CA's root certificate. We'll start with an RSA key:

cd ${DIR}
openssl req -newkey rsa:4096 -x509 -days 7305 -extensions v3_ca -keyout private/cakey.pem -out cacert.pem
chmod go= private/cakey.pem

The filenames here are mandatory, they're what openssl.cnf is set up to expect. You want to set the permissions on the key to make doubly sure nobody but you can read it. You'll be prompted to enter a number of bits of information. Most important is the passphrase. The key's encrypted to keep it secure if someone happens to gain physical access to the key file. Pick a strong password, don't be afraid of having it be hard to type, you'll only be using it when you generate new certificates. Resist the urge to not have one, you really want the master key locked down. The country, state and locality should be your country, state and city. Organization name should be your company or organization. For the CA I use the domain name as the organizational unit, but it's not truly required. For the common name I use something like the organization name followed by "CA RSA root key" to tell me exactly what kind of key this is. The e-mail address should be one that goes to whoever's going to be responsible for certificates. Note that this certificate'll be valid for 20 years. That's not a mistake. Issuing new root certificates is a pain so you don't want to issue them often, and the key (the vulnerable part) never needs to leave it's isolated little environment so it's easy to keep secure.

Don't worry too much about getting it completely right the first time. You can always delete the cacert.pem and private/cakey.pem files and do it again. That's the joy of having your own little sandbox, mistakes are relatively cost-free (or at least low-cost).

Once you've got your CA key, it's time to create your first server certificate. For these, organizational unit is good to have filled in reasonably. Fill it in with the department or purpose of the certificate, eg. "Postfix SMTP server certificate". The domain name's also good for Web server certificates. The common name's the important one. Client software typically checks the CN of the server's certificate against the hostname it was trying to contact, and rejects the server if the two don't match. This protects against someone trying to impersonate your server or redirect traffic to their own server. It does mean that you can't create a certificate with a CN of "abc.example.com" and expect it to work for "www.example.com". The CN in the certificate has to match the DNS name clients are going to try to connect to. The exception is wildcard certificates: "*.example.com" will be accepted for any hostname in example.com. As a general rule for things like mail servers you'll want to use an exact hostname matching the expected server name. Web servers are the ones where you'll need wildcard certificates the most, to accomodate name-based virtual hosts. Try to avoid wildcard certificates if you can, only use them when you truly need SSL on a single host answering to multiple names. And note that it only works within a single domain. You can't create one certificate that'll work for "abc.example.com" and "xyz.example.org". And while you could create one for "*.com" to answer to multiple .com domains, this is a bad idea because it'll only work internally. Due to security and interception concerns, you'll have a hard time finding a legitimate CA that'll issue you that kind of wildcard certificate when you want to go public. If you've built your system to depend on it, you'll be left with no options.

OK, enough with the lecture. We'll use ${name} to stand in for the name of the certificate file. Usually I use the server name (or the domain name for wildcard certificates), or a one-word name like "dovecot" or "postfix" for certificates intended for the Dovecot IMAP server or Postfix SMTP server software. Commands:

cd ${DIR}
openssl req -newkey rsa:4096 -nodes -days 730 -keyout private/${name}-key.pem -out newreq.pem
openssl ca -in newreq.pem -out certs/${name}.pem -notext
rm newreq.pem
chmod go= private/${name}-key.pem

The -nodes option says to not put a password on the key. This isn't secure, there's no protection between someone with access to the key file and the key itself, but server software can't prompt for the password upon startup so you have to leave the password off. The -days option says to make the certificate valid for 2 years. That should be long enough to not swamp you with reissuing certificates constantly. The newreq.pem file is an intermediate step: the "openssl req" command generates the key and a certificate signing request, then the "openssl ca" command signs the certificate using the root CA certificate you generated earlier and puts the signed certificate in the certs directory. newreq.pem isn't needed once the certificate's been signed, so it gets deleted and we make sure to secure the permissions on the key file.

You'll notice that OpenSSL also added a copy of the certificate in the "newcerts" directory with a name based on the serial number from the "serial" file. I let it do that to keep a record of every certificate I've issued. I let "openssl ca" maintain the index database, the "serial" file and the "newcerts" directory, I don't mess with them.

If you want the certificate information in human-readable form, add this command:

openssl x509 -in certs/${name}.pem -out certs/${name}.txt -text

That'll create a .txt file companion to the .pem file with the certificate's information in human-readable form followed by the actual certificate data. SSL software doesn't care about the human-readable text, so generally I leave the .pem file with just the certificate data and never bother with the .txt file. If I need to see it I can always run that command to generate the text.

Installing server certificates depends on the software involved. If you aren't familiar with the software's documentation and where to find out how to configure SSL, start reading. You'll need to know it to get SSL working correctly.

We're missing two steps yet. The first is to put your CA certificate into OpenSSL on your machine so it's usable. Wherever your openssl.cnf file was, there'll be a "certs" directory there. If it's a normal OpenSSL installation, there'll be a lot of root certificates already in there. Pick a name for your certificate file, for example "BudsToolsCACert", and copy "certs/cacert.pem" over to that OpenSSL certs directory as "BudsToolsCACert.pem". Make sure it's readable by everyone. Then run the "c_rehash" command to regenerate the housekeeping information to include your new certificate. That's it. Now OpenSSL on your machine knows about your CA and can verify certificates you've issued with it.

The last step is to import your CA certificate into client software like your Web browser or e-mail client. Some software uses the system OpenSSL certificate store, so once you've installed your CA certificate there that software will work automatically. Other software, eg. most Web browsers or the Thunderbird e-mail client, maintain their own certificate stores. The process varies. For current Firefox, for instance, go into Options, Advanced, the Encryption tab, and hit the View Certificates button. In that dialog go to the Authorities tab, hit the Import button, browser to the cacert.pem file, select it and hit OK. That'll import your certificate and the Firefox will be able to accept any certificates issued by you without any complaining. The process for Thunderbird, Internet Explorer and other software should be similar.

DSA-based certificates

There's another algorithm that can be used for keys and certificates: DSA. Normally you want to use RSA for your certificates, but if you want to generate DSA certificates for some reason the basic steps are similar. The big difference is that first you have to generate DSA parameters, and then you can use those parameters to generate a DSA key and certificate:

openssl dsaparam -out private/dsaparams.dat 4096
openssl req -newkey dsa:private/dsaparams.dat -x509 -days 7305 -extensions v3_ca -keyout private/cakey_dsa.pem -out cacert_dsa.pem

That'll give you a CA certificate and key using the DSA algorithm instead of RSA. Note that the "openssl ca" command won't automatically use this since the key filename isn't what it's configured to use. To generate server certificates:

openssl req -newkey dsa:private/dsaparams -x509 -days 730 -keyout private/${name}-key.pem -out newreq.pem
openssl ca -keyfile private/cakey_dsa.pem -in newreq.pem -out certs/${name}.pem -notext

The -keyfile argument forces "openssl ca" to use the DSA CA key rather than the default RSA one, and we're reusing the DSA parameters we generated initially. They're very time-consuming to generate, and as far as I know there's no security implications to using the same parameters for multiple certificates. What I do is generate one set of parameters to use for generating root certificates, and a second set for generating server certificates. If you're truly paranoid you can generate a new set of parameters for each certificate you create, but I think that's going overboard.

DSA isn't recommended. It's slower doing signature verification, and the mathematical problem underlying it hasn't been as thoroughly worked over as the one underlying RSA so there's less known about how hard it is to crack the keys and it's considered to be much weaker. It's also supported by fewer SSL implementations, so if you use DSA-based certificates you could end up being unable to talk to some client software. RSA-based certificates are almost universally supported.