/ tls

TLS Client Authentication with Certificates (nginx + easyrsa)

We begin by picking a machine (or container) we will be using as a certificate authority. Because this machine is charged with generating certificates we will be using as trusted authentication factors, it is important that this machine is well secured.

We begin by downloading easyrsa3

easy-rsa is a set of shell scripts that simplify managing a public-key infrastructure of certificates and keys. It calls the openssl binary.

# If you don't have git
# wget https://github.com/OpenVPN/easy-rsa/archive/master.zip

$ git clone https://github.com/OpenVPN/easy-rsa

Generate our PKI folder

$ cd easy-rsa/easyrsa3 && ./easyrsa init-pki

Edit variables

By default, our client certificates are set to have an expiration date of 10 years. This is waay too long.

We set our CA to expire in two years and our client cert to expire in one year by setting the following variables

$ cp vars.example vars
$ nano vars

Generate our CA

Make sure you pick a secure password when prompted

$ ./easyrsa build-ca

Generate Certificate Revocation List

A certificate revocation list is a digitally signed list of certificates that have been revoked. In a secure, production environment, we store the key for the crl on a separate machine than the CA. This way, if the CA is compromised, we can still issue out the revocation so our browsers and web servers know the key should not be trusted. For our purposes, we can just keep it in our easy-rsa/pki working directory.

$ ./easyrsa gen-crl

Generate our first certificate

We will later install this on our browser.
This certificate will be required to access our protected https server.

$ ./easyrsa build-client-full rohan-laptop nopass
$ ./easyrsa export-p12 rohan-laptop

We then install the PKCS12 bundle to our browser. The PKCS12 bundle contains both our certificate and key

Importing into the browser is relatively trivial. For both firefox and chrome you go into
settings -> advanced -> manage certificates -> your certificates -> import

Setting the server to require authentication

From our CA box, we scp our CA certificate and our CRL to our webserver. Also, it's worth mentioning, that unlike keys, these files are not strictly confidential. If paranoid (or sensible), you may want to keep a hash of these files

$ scp pki/ca.crt  root@webserver:/etc/ssl/nginx_ca.crt
$ scp pki/crl.pem root@webserver:/etc/ssl/nginx_ca.crl

First, make a backup of the nginx config file. Then edit it, for simplicity sake, we'll not assume virtual hosts as that can potentially complicate things (you can do ssl/tls with vhosts with a feature known as SNI, but I'm not completely certain it works fine for client side tls)

In the nginx settings, we have two sections for client and server certificates

ssl_protocols       TLSv1 TLSv1.1 TLSv1.2;
# Certificate for standard HTTPS
ssl_certificate     /etc/ssl/fakedomain.etherarp.net/fakedomain.etherarp.net.crt;
ssl_certificate_key /etc/ssl/fakedomain.etherarp.net/fakedomain.etherarp.net.key;

# CA for verifying client cert 
ssl_client_certificate /etc/ssl/nginx_ca.crt;
ssl_crl /etc/ssl/nginx_ca.crl;

# Client must possess a certificate verified by the CA
ssl_verify_client on;

Now restart nginx. Now, if your certificate is not properly installed in your browser, you will get an error message of the 400 response. (Use a different browser to be sure of this). Otherwise, with the cert installed, your browser should notify you to say the site has requested a certificate.

Final note, you cant use https client authentication on a per-location basis because the TLS handshake occurs before any dialogue with the webserver (GET, POST etc) takes place.

That's all. Hope this was helpful