Client Side Certificates for Web Apps
October 19, 2011
After figuring out how to call a web service from Java using client side certificates, I was keen to try and set up one of my own, using nginx.
Create Certificate Authority
The first thing to do is create the CA certificate, a role normally performed by one of the commercial certificate suppliers. Doing it ourselves is good for testing, but shouldn't be used for production.
We need to create a key, and then generate a certificate from it:
$ openssl genrsa -des3 -out ca.key 4096 $ openssl req -new -x509 -days 365 -key ca.key -out ca.crt
Our ca.crt file is valid for 365 days.
Create Server Side SSL Certificate
Next, we need to create another key. This will be used to generate an SSL certificate for use on the server. Make sure that the certificate contains the url it's going to be used at (for example drumcoder.co.uk). This should go in the Common Name (eg, YOUR name)
field. Don't include any https:// prefix.
$ openssl genrsa -des3 -out server.key 4096 $ openssl req -new -key server.key -out server.csr
Next, we need to sign this key with our CA certificate:
$ openssl x509 -req -days 365 -in server.csr -CA ca.crt -CAkey ca.key -set_serial 01 -out server.crt
Our new server.crt
is valid for 365 days.
The serial field is used to ensure that the certificate can be superseded by a new one later on.
We can remove the pass phrase from server.key
so that it isn't required each time the web server is restarted:
$ openssl rsa -in server.key -out server.key.insecure $ mv server.key server.key.secure $ mv server.key.insecure server.key
Create Client Side Certificate
At this point we have enough to put a normal server side certificate on the web server, but we want to go further than that. We want our server to require the presence of a client side certificate, signed by the same ca.crt
, before allowing access to the web application.
First we generate another new key and certificate:
$ openssl genrsa -des3 -out client.key 1024 $ openssl req -new -key client.key -out client.csr
Make sure that the certificate does NOT contain the url it's going to be used at (for example drumcoder.co.uk). Put your name in the Common Name (eg, YOUR name)
field. Don't include any reference to the url being accessed.
We then sign this new certificate with our ca.crt
:
$ openssl x509 -req -days 365 -in client.csr -CA ca.crt -CAkey ca.key -set_serial 91 -out client.crt
Note that we're using a different serial. If you use the same one as the server SSL certificate, then this will cause problems when you come to import it into the browser.
We're now going to convert this client.crt
into client.p12
ready for import into Firefox.
$ openssl pkcs12 -export -in client.crt -inkey client.key -out client.p12 -name "drumcoder"
Copy client.p12
from the server to the client, and import it into Firefox using Advanced/Encryption/View Certificates/Import
. The certificate should show up on the Your Certificates
tab.
nginx
It's now possible to set up an https server in nginx. Simply copy an existing config and change the following fields:
server { listen 443; ssl on; server_name server.baal; ssl_certificate /etc/nginx/certs/server.crt; ssl_certificate_key /etc/nginx/certs/server.key; ssl_client_certificate /etc/nginx/certs/ca.crt; ssl_verify_client on; ... }
ssl_certificate
and ssl_certificate_key
are used for normal https access.
ssl_client_certificate
points to the certificate that must have signed the client certificate for access to be granted.
ssl_verify_client
turns on client certificate validation. Without a client certificate present, nginx will return a 400 error.