Saturday, June 9, 2012

Automating the Creation of a Certificate Authority and Issuing Certificates with OpenSSL

I spent a little time over the weekend working on securing an Node.js server with SSL client-authentication.  I needed to create a certificate authority from which I could create and sign both server and client certificates for use by my application.

While the process is not terribly difficult, there is a lot to know (and remember), I decided to automate the it a little bit and thought others might be interested.  I should mention up front that this website (full link below) was especially helpful in understanding the process.

I will start by explaining the process of creating a CA and issuing certificates.  Once you understand the process, I will describe how you can customize some of that process using OpenSSL config files.  I will conclude with describing some automation steps that can be performed to make this process easier.

I've created a public Github repo with the scripts mentioned in this post:  https://github.com/berico-rclayton/certificate-automation/.

Creating a Certificate Authority


Each step will require some input (the commands are interactive).  You will set the CA's password in the first step, and this password will be use in many of the other steps (including signing certificates for servers and clients).  Needless to say, remember this password (but keep it safe -- this is quite literally the keys to the castle).

1.  Create a key for the certificate authority.
openssl genrsa -des3 -out ca.key 1024
2.  Create a certificate request for the authority:
openssl req -new -key ca.key -out ca.csr
3.  Create a certificate for the CA, by signing (self-sign) the certificate with the CA's own key:
openssl x509 -req -days 365 -in ca.csr \
    -out ca.crt -signkey ca.key

Create a Server Certificate


1.  Create key for the server.
openssl genrsa -des3 -out server.key 1024
2.  Create a request for certificate.
openssl req -new -key server.key -out server.csr
3.  Sign (CA) and issue the certificate.
openssl x509 -req -in server.csr -out server.crt \
    -CA ca.crt -CAkey ca.key -CAcreateserial -days 365

Create a Client Certificate


1.  Create key for the client.
openssl genrsa -des3 -out user.key 1024
2.  Create a request for certificate.
openssl req -new -key user.key -out user.csr
3.  Sign (CA) and issue the certificate.
openssl x509 -req -in user.csr -out user.crt \
     -CA ca.crt -CAkey ca.key -CAcreateserial -days 365
4.  Create a PKCS#12 formatted certificate for use in browsers like Firefox.
openssl pkcs12 -export -clcerts -in user.crt \
    -inkey user.key -out user.p12

Customizing OpenSSL Configuration


If you execute these commands, you will soon discover how annoyingly interactive the process is (lots of stuff to input just to create a certificate).  Fortunately, the CA process is a one time deal (well as long as you issued your own CA certificate).  Generating a server certificate will also probably be a relatively infrequent process.  It's the issuing of client certificates that becomes a real pain in the ass, which is why I automated this process.

There is an out-of-the-box way to automate a little bit of this work using custom OpenSSL configuration files.

1.  In the directory of your choice, create a text file called "openssl.cnf".
2.  Add configuration values to the newly created file.

Copy and paste from here: https://github.com/berico-rclayton/certificate-automation/blob/master/conf/server_openssl.cnf
 
# ... truncated ...

[ req_distinguished_name ]
0.organizationName = Organization Name (company)
organizationalUnitName  = Organizational Unit Name (department, division)
emailAddress  = Email Address
emailAddress_max = 40
localityName  = Locality Name (city, district)
stateOrProvinceName = State or Province Name (full name)
countryName  = Country Name (2 letter code)
countryName_min  = 2
countryName_max  = 2
commonName  = Common Name (Computer Name, Server, or Username)
commonName_max  = 64
 
0.organizationName_default = Berico Technologies
organizationalUnitName_default  = Engineering BU
localityName_default  = Reston
stateOrProvinceName_default = Virginia
countryName_default  = US
emailAddress_default  = server@bericotechnologies.com
commonName_default  = server
 
# ... truncated ...
3.  Edit the values (right-hand side of "=" sign) of the variables suffixed with "_default" to change the default values displayed in the OpenSSL commands.  There are also a number of other important variables like default "key length" (we've been using 1024 bits in this example) that you can also set.

4.  Ensure OpenSSL uses your new config file.
export OPENSSL_CONF=openssl.cnf

Automating the Process


We've gotten as far as we're going to get with configuration, so lets use a little BASH magic to automate some of this process.  The first thing we will do is organize our directory structure instead of writing all files to the current working directory as I have done in the previous examples.  I'm going to use a directory structure almost identical to the one suggested by the website listed above (and below):
  • ca/ - holds certificate authority's certificate, certificate request, and key.
  • server/keys/ - holds all keys generated for servers.
  • server/requests/ - holds all certificate creation requests to the CA for servers.
  • server/certificates/ - holds all certificates for servers.
  • user/keys/ - holds all keys generated for users.
  • user/requests/ - holds all certificate creation requests to the CA for users.
  • user/certificates/ - holds all certificates for users.
  • user/p12/ - holds all PKCS#12 formatted certificates for users.
By the way, make sure you protect these folders judiciously (owned and accessed by root only).

I've created three BASH scripts for automating this process:

1.  create_certauth.sh - creates the directory structure and Certificate Authority's key and self-signed certificate.  This will probably only be executed once for an organization.
sh setup_certauth.sh
2.  make_server_cert.sh - creates a certificate for a server.  You will need to specify the server name, which will be used as the file name for all artifacts.
sh make_server_cert.sh {server-name}
3.  new_client_cert.sh - creates a certificate for a user, along with the PKCS#12 certificate, which is used by some browsers.  You will need to specify the client's name, which is used as the file name for all artifacts.
sh new_client_cert.sh {client-name}

Screenshot of the client certificate creation process.


Viola.  I should mention that I am not a BASH Ninja, so if you have some suggestions, I would love to hear them.  The scripts in the Github repo will use the directory structure to gather the CA certificate, as well as, store the keys, requests, and certs.

Hope this was useful; if not, let me know.

References:


Special Thanks to the Author of this Guide:  http://www.cafesoft.com/products/cams/ps/docs30/admin/ConfiguringApache2ForSSLTLSMutualAuthentication.html#Creating_a_Certificate_Authority_using_OpenSSL

2 comments:

  1. When you create a cert request you still have to answer all the config questions. E.g.
    Country Name (2 letter code) [US]:

    Is there any way to avoid it?

    ReplyDelete
    Replies
    1. Igor, great question. I found this Super User (e.g. Stack Overflow) post article with the correct command. I'm going to update the Git Repository to include the correct command:

      http://superuser.com/questions/226192/openssl-without-prompt
      http://superuser.com/questions/407874/openssl-csr-generation-with-subject-key-from-stdin

      Delete

Note: Only a member of this blog may post a comment.