Sunday, March 11, 2018

Creating Long Term SSL Certificates

"It's constantly fascinating for me that something that feels absolutely right one year, 12 months later feels like the wrong thing to do." --Damian Lewis
Often I find myself having to create my own SSL certificates. Be it an internal web-server, or two scripts that need to communicate to each other, SSL is the easiest way to encrypt network traffic. Unfortunately it's also one of the most dangerous encryption methods. If you make a mistake setting it up it usually works ... at least for a little while.

Ignoring the client SSL checks for now, (hint if your script is using SSL and it works the first time, you probably are not checking SSL correctly), one area of danger is having your SSL certificates expire. As an example of that, recently every Oculus Rift broke because a code signing certificate expired. Admittedly this was a different type of certificate, but the same thing tends to happen with internal SSL deployments. People do not remember to update them, and when they expire things tend to break, (at least if your clients are checking SSL properly). The problem is when you use the standard OpenSSL libraries to create your certificates, there's three places that you need to specify certificate lifetimes. If you forget to specify any of the three, the certificate will be valid only for the default which is set to be "365 days".

These lifetime checks are:
  1. The Certificate Authority has an expiration date
  2. The actual certificate you are using has an expiration date
  3. The CA signature for the certificate has an expiration date
Since most stack-overflow posts don't cover this, and Linux man pages are not helpful unless you already know what you are doing, I wanted to share my cheat sheet for creating long term, (valid for one thousand years), SSL Certificate Authorities and signing certs. This script was born from many previous failed efforts, and to be honest I'm still not sure I have it perfectly right. If you notice any improvements that could be made, please let me know! 

Requirements/Comments:
  • These instructions were written for CentOS. It should work for most other Linux flavors without any changes. If you are using Windows, good luck!
  • OpenSSL
  • Whenever you see 365000 in the command that's the expiration date. I'm using 365*1000 as shorthand for one thousand years. Yes I realize that isn't exactly accurate. Feel free to change this to the time period you want to use.

Creating the Certificate Authority: (If you already have a CA ignore this, but you might want to check the valid lifetime for that CA)
  • Generate the key for the CA using 4096 RSA. Note the key will be cakey.pem so protect that!
openssl req -new -newkey rsa:4096 -nodes -out ca.csr -keyout cakey.pem
  • Create the CA's public certificate which will be called "cacert.pem". Note the '-days' field:
openssl x509 -trustout -signkey cakey.pem -days 365000 -req -in ca.csr -out cacert.pem
Important: When you run the previous command, you'll be set a list of questions. Note, for many SSL deployments you *must* have the Country, City, State, and Organization match between your CA and the certificates you are signing. Does this make sense? Of course not! The domain can be pretty important as well depending on what you are doing.
  • Next you need to copy the CA info and create the required files into where OpenSSL expects them. Yes if you know what you are doing you can override the defaults, but if not here's what to do:
    1. If the /etc/pki/CA directly does not exist, create it
    2. mv cakey.pem /etc/pki/CA/secret/cakey.pem
    3. touch /etc/pki/CA/index.txt
    4. create or edit /etc/pki/CA/serial using the text editor of your choice
    5. In this file put a list of all the serial numbers you want to assign certificates, separated by a newline. For example:
      • 01
      • 02
      • 03
      • 04
    6. It is *highly* recommended that you set permissions on the /etc/pki/CA directory so only the user you want to sign certificates has access to it.
  • Note, cacert.pem is not used for signing SSL certificates, but you'll need to push it to clients that are verifying the certificates

Creating and Signing a SSL Certificate:

  • Create the certificate private key using RSA 4096. It is named client.key in this example. Make sure you protect this!
 openssl genrsa -out client.key 4096
  • Create the certificate request. Note the "days" field.
openssl req -new -key client.key -out client.csr -days 365000  
Important: Remember for the questions it asks you, the Country, City, State, and Organization *must* match between your CA and the certificates you are signing. In addition, the domain can be pretty important depending on if you are checking that with your client or not
  •  Create the actual client certificate. Once again, note the '-days' field
openssl ca -in client.csr -out client.pem -days 365000 -notext

Resulting Files: 
  1. Public Client Certificate: client.pem
  2. Client private key: client.key (Only deploy on the server that owns this key)
  3. Public CA certificate: cacert.pem
  4. Private CA key: cakey.pem (Protect this one!!)