Sunday, October 19, 2014

Learn HTTPS by becoming your own CA

Chain of trust

HTTPS depends on a chain of trust, and each step in the chain consists of a certificate that has been signed by its parent certificate. Usually the chain is 2~3 nodes long. The first node in the chain is one of around 50+ Certificate Authorities (CA) certificates configured to be trusted by your computer (and basically all computers world-wide). The last node in the chain is the certificate identifying the domain name of the HTTPS website you are visiting.

Looking at https://www.google.se for example, the chain of trust starts from GeoTrust Global CA, which is already trusted by most computers by default. They then signed Google Internet Authority G2, and specifically allowing Google to sign other certificates on the behalf of GeoTrust. Google then signed a certificate with a Common Name (CN) of *.google.se, which matches the domain name(s) this certificate represents.



Certificate

A certificate is basically a public key, bundled with some key-values (like an expiration-date and, most importantly, a Common Name, CN), and appended with a hash of these things encrypted with the private key that signed the certificate.



In many cases the Common Name in a certificate is the name of a company (like GeoTrust), but for the website certificate (last-node in the chain) the CN must match the domain name (as seen above for *.google.se, which is a wildcard match for www.google.se among others). Wildcard certificates are convenient since they can be reused for any domain names that matches.

Each certificate has a corresponding private and public key.



Certificate sign request (CSR)

Signing a certificate requires two peers (for example: you and GeoTrust) to co-operate. Each of the two peers has their own private key they need to keep secret. You want to send your public key and some key-values (such as the domain name in Common Name (CN)). To do this you create a Certificate sign request (CSR), which is basically a certificate with a missing signature.



When GeoTrust gets your CSR they can do some research to see that you actually own that domain name (usually by sending a verification e-mail to hostmaster@your-domain.com). They can also look up your company in a public directory and/or give you a phone call. When satisfied, they add some key-values such as valid from, valid to and purpose of certificate. (For example, the certificate Google Internet Authority has an extra purpose which gives it right to sign other certificates - but the *.google.se does not.) After adding their signature by (hashing and encrypting that hash with their private key) they send you back a complete certificate.

Let's play Pretend Certificate Authority

First install OpenSSL and Nginx. Put openssl on path and open a terminal to the Nginx conf folder.

1.1) Generate your CA private RSA key (root.key)

openssl genrsa -out root.key 2048

Here the key will be of size 2048 bits (default: 512). And the private key will not be password protected. (Password protection only benefits security if you have a password that can resist trillions of brute-force attempts per second, assuming you don't store it in plain-text in your server config which you probably would do anyway :).

1.2) Generate your CA certificate sign request (root_req.csr)


openssl req -new -key root.key -out root_req.csr -sha256

You now need to fill in some info, but the most visible in the Common Name (CN).
I wrote Johan Deckmar as my CA Common Name. After completing all the steps, this is what the browser will show:

(Final result)

1.3) Self-sign the root_req.csr with the root private key to get the CA root certificate (root.crt)

openssl x509 -req -days 365 -in root_req.csr -signkey root.key -out root.crt -sha256

Alright. Half way there, almost. Currently the root.key needs to be kept private. And the root.crt is a public certificate that you want people to trust as a root CA certificate. If you are doing this at your company, it's the root.crt that needs to be installed as a root CA cert on the employees computers.

Now, add the root.crt as a trusted CA root cert on your computer.

In Windows: Double-click on it --> Install certificate --> Next --> Select destination: Trusted root certificate authorities --> Next --> Complete.

For managing certs in Windows in general: Start --> Run: certmgr.msc

2.1) Generate your web-server private RSA key (server.key)


openssl genrsa -out server.key 2048

2.2) Generate your web-server certificate sign request (server_req.csr)


openssl req -new -key server.key -out server_req.csr -sha256

As before, fill in some info about your company and web-servrer. NOTE: Set the Common Name (CN) to a domain name (or wildcard) matching your webserver (where you have Nginx). If you are just trying this on your local computer, you can use the following DNS which points to your local IP-address:  localhost.deckmar.net


2.3) Sign your web-server CSR with CA private key to make the cert (server.crt)


openssl x509 -req -in server_req.csr -CA root.crt -CAkey root.key -set_serial 02 -out server.crt -days 365 -sha256

2.4) Concatenate your web-server cert and the root CA cert to form a chain (server.pem)


Soon when your web-server (Nginx) identifies itself with an HTTPS certificate, it should send the whole chain of certificates starting from the web-server cert and finishing with the root CA cert. This might sound complicated, but is actually done by concatinating the certificate files into one file like this:

cat server.crt root.crt > server.pem

The file-ending doesn't actually matter, but I use the file name server.pem for a file with multiple certificates. (PEM is a format which can contain one or more certificates concatenated after each other).

3) Configure and start Nginx


Now open conf/nginx.conf and go down and un-comment the configurations under # HTTPS server.

I made the following three changes in nginx.conf:

ssl_certificate      ../secure/server.pem;
ssl_certificate_key  ../secure/server.key;

ssl_session_cache    none;

The path to the .pem and .key file is relative to the conf folder, so as you can see I put my keys and certs in a new folder called secure in the nginx folder.

Start Nginx. Assuming everything was done correctly you should now get a green lock when you open:
https://localhost.deckmar.net

Since I used mainframe.deckmar.net as the domain name, this is what I see when inspecting the certificate on my server (click the green lock --> certificate information).


In some cases you need to restart your browser completely to use the latest certificate settings on your computer.

Let me know how if goes if you try this out yourself. Especially if you find some mistake in the commands or something missing/wrong in the explanations.

Cheers!