Friday, 18 January 2019

Self-Signing SSL/TLS Certificates

Things have changed a bit since I last looked into setting up a Certificate Authority (CA) and using that to self-sign my own certificates, not least that the use of the Common Name (CN) field appears to have changed. Chrome in particular seems to insist on the use of the Subject Alternative Names (SAN) extension rather than (or in addition to) using the CN field. So these are my notes on how to set up your own CA and use that to sign certificates. I'm conscious this is bound to go out of date so at the time of writing I'm working with Firefox 64, Chrome 71 and OpenSSL 1.1.1.

First of all, create a config file along the lines of the following and call it anything you like but for these notes I'm going to call it ssl.conf.  Note, if you want to you can start with a different template or look at your own openssl.cnf file which on Linux is commonly found at /etc/pki/tls/openssl.cnf.

[ req ]
default_bits       = 4096
distinguished_name = req_distinguished_name
req_extensions     = req_ext

[ req_distinguished_name ]
countryName                 = Country Name (2 letter code)
countryName_default         = GB
stateOrProvinceName         = State or Province Name (full name)
stateOrProvinceName_default = England
localityName                = Locality Name (eg, city)
localityName_default        = MyCity
organizationName            = Organization Name (eg, company)
organizationName_default    = MyOrg
commonName                  = Common Name (e.g. server FQDN or YOUR name)
commonName_max              = 64
commonName_default          = localhost

[ req_ext ]
subjectAltName = @alt_names

DNS.1 = localhost

You can change any of this template and indeed you'll need to change the common name for the certificates you're generating. The CN can be changed either on the command line during certificate creation or by changing the default in ssl.conf. You will also need to change the list of names under the "alt_names" section, this list should contain one line for each host name your machine might be known as. The list starts at DNS.1 for the first entry, then you can add DNS.2 for the second entry and so on.

NOTE: the specification and a lot of the documents available in this space indicate that an IP address can be used in the CN.  My testing seems to indicate that while this is the case, certificates produced in this way will be rejected by modern browsers.  Hence, you should list only hostnames as the CN but IP addresses still appear to be acceptable in as "alt_names".

Create a Certificate Authority
You'll need a certificate and key file to act as your own CA:

openssl genrsa -out RootCA.key 4096
openssl req -x509 -new -nodes -key RootCA.key -sha256 -days 3650 -out RootCA.pem -config ssl.conf

You can inspect the certificate with:
openssl x509 -in RootCA.pem -text -noout

Create a Certificate Signing Request (CSR)
Now you have a CA you can create a CSR that can be used with your CA certificate to generate a client certificate:

openssl genrsa -out server.key 4096
openssl req -new -key server.key -out server.csr -config ssl.conf

You can inspect the certificate with:
openssl req -text -noout -verify -in server.csr

This time it's really important to ensure your host names are listed under the "X509v3 Subject Alternative Name" section of the certificate.

Generate a Signed Certificate
You can now use the CSR to create a signed certificate that can be used to serve up content over a secure connection:

openssl x509 -req -in server.csr -CA RootCA.pem -CAkey RootCA.key -CAcreateserial -out server.pem -days 3650 -sha256 -extensions req_ext -extfile ssl.conf

Note: if you want to create a different format of certificate here you can simply replace server.pem in the above command with something like server.crt, for example.

You can inspect the certificate with:
openssl x509 -in server.pem -text -noout

Again, it's really important to ensure your host names are listed under the "X509v3 Subject Alternative Name" section of the certificate.

Use the Certificate Server Side
You can now put your server.pem and server.keyfiles to work and serve up content over a secure connection.  There's too many ways to do that to list here but it could be used with a web server to serve HTTPS or a websockets server to serve some sort of socket connection as a couple of examples.

If you want to inspect the certificate that's being used on the server, replace <hostname> and <port> in the command below:

openssl s_client -connect <hostname>:<port> | openssl x509 -noout -text

Use the Certificate Client Side
My use case here is with a web browser and so you'll want to import your <i>RootCA.pem</i> into your browser environment.  There are two main ways of achieving this, you can either:
  1. Import directly to the browser
  2. Import to the key store on your operating system
It's quicker and easier to import directly to the browser but this will of course only cover that one browser on your system whereas if you use the operating system method then any application that consults the OS for certificates will see your CA certificate.

For Firefox, go to "View Certificates" in the preferences; click the "Authorities" tab and then the "Import" button; select your <i>RootCA.pem</i> file and click OK.

For Chrome, go to "Manage Certificates" in the settings; click the "Authorities" tab and then the "Import" button; select your <i>RootCA.pem</i> file; click the check boxes to trust the certificate and click OK.