How to do a mutual ssl authentication at reverse proxy level

Hello Guys,

In this tutorial we are going to implement 2 way or mutual ssl authentication. I would love to keep this post short and straight to the point so it assumes that you understand what SSL is for and what we are aiming for. But in every day english it means 2 parties authenticating to each other using an SSL certificate so both parties are assured of each other’s identities.

How does that work ?

Most of the time a web application has an SSL on its url and our browsers know how to verify that authenticity and let us through. Our browser which is a client to that web application has been able to verify the web application authenticity, that’s one way. The other way of the mutual ssl authentication is to make the web application able to authenticate its clients. Clients could be anything from a curl command, a python, java, ruby etc application as well as a simple browser. When that’s done we have a mutual ssl authentication.

There are several ways of doing this but we will use a simple but yet solid approach to implement this. A web server running in front of our actual application be it java or python ruby etc. We will use a spring boot application in this tutorial.

Requirements

  • A Simple Spring boot application : A simple application that queries Internet Chuck Norris jokes database and runs on port 8080
  • A nginx web server : Our web application which runs on port 80 and passes requests to the spring boot app. nginx is our reverse proxy.
  • CompanyACA.crt :Company A Certficate Authority used to sign companyA clients’ certificates
  • CompanyBorSomeCA.crt : Company B or Some Certificate Authority like verisign, digicert, thawte, geotrust used to sign jokes.mycodingpains.com server certificate
  • jokes.mycodingpains.com.crt : SSL certificate for our application running at jokes.mycodingpains.com
  • nejifromcompanyA.crt: client SSL certificate from companyA and signed by companyA to make call to our jokes application.

Our Scenario

We have a small java based app that makes requests to the Internet Chuck Norris Database to retrieve chuck norris famous jokes. It accepts an integer number on the application/x-www-form-urlencoded param “count” and retrieves the corresponding number of jokes. Nothing fancy ,with limited features and error handling. We want to make that application accessible from the subdomain jokes.mycodingpains.com. Because I didn’t want to handle adding certificate stuff to java nor dreaming about adding client’s certificate to java truststore, I would rather let nginx worry about that headache for me.

We want to only allow trusted client to be able to access those jokes so we will implement a mutual ssl authentication between the jokes app and any client that wants/needs access. Here comes companyA which we want to give access to. We will ask company A to sign its clients and send us an authenticating certificate we can use on the jokes app side to validate the client’s authenticity. Let’s see step by step how to make that happen

Generating everything client side: CompanyA

CompanyA Root Certificate

certificate key

Using openssl we are generating the key needed for the Root Certificate. This root certificate act as Certificate Authority for companyA.

I used a pass phase on the key which is 1234567890

Creating CompanyA Certificate

Let’s create the actual Root certificate

Client Certificate

certificate key

Let’s create the key for the client certificate

No pass phrase is used here notice -des3 is taken out from the genrsa command.

Creating nejifromcompanyA client Certificate

Signing of Neji client’s certificate by companyA using CompanyACA.crt

We will now use the Root Certificate from company A to sign the client generated by neji

Now that we are done with the client’s certificate. Let’s see how server side is going to be like

Generating everything server side: CompanyBorSomeCA

Root Certificate

This step is not needed if you already have a signed certificate by any big and recognized Certificate Authority.

Creating the CompanyBorsomeCA key

For the key pass phrase I used 0987654321

Creating the Root Certificate

Notice here as well, I have taken out the request for pass phrase

Creating our joke server certificate signing request

We can now proceed with our signing of the certificate. If we were to buy digicert or verisign etc certificate, we would have submitted this CSR to be signed and a Certificate sent to us back. But we are our Own Certificate Authority or should I say CompanyB is our mycodinpains server CA.

Signing the server certificate with CompanyBorSomeCA

Running our App

Now that we have all our requirements done, let’s upload our spring boot application . We will need to also upload some certificates. CompanyACA.crt, jokes.mycodingpains.com.crt, jokes.mycodingpaons.com.key and install nginx server

Let’s install java in order to run our app courtesy of webupd8 group

Running our app is then simple

Let’s create our virtual host for running our jokes app

Let’s have its content similar to the configuration below, that’s depending on name used during the tutorial and the domain name used

Let’s enable our virtual host and reload the config

Notice: mutual ssl certificate has been disable on line 12,13,14 . This allows us to test our setup first

Testing With one way SSL

Browser

Accessing the site with normal SSL enabled
Accessing the site with normal SSL enabled

The image above shows normal spring boot error page . This means our ssl is working. Let’s try make some request to our jokes app using http requester

Call using httprequester

cURL

Let’s make further call using curl

Testing with 2 way SSL without Client certificate

Browser

Let’s remove the comment from the configuration at /etc/nginx/sites-available/jokes.mycodinpains.com.conf and restart the nginx server.

Accessing the site with 2 way SSL enabled
Accessing the site with 2 way SSL enabled

Our request was rejected because our browser did not present the adequate client’s certificate to server. Same thing happens with curl request below:

cURL

Testing with 2 way SSL with Client certificate

Browser
In order to test this on a browser, we need a PKCS12 format of our certificate. Let’s then create a pfx format of our certificate

I used 1234 for the export password. That’s the password we will use when adding our pfx to our browser

I am using Mozilla Firefox on a linux machine so preference it at : Edit > Preferences > Advanced > View Certificate > select “Your Certificate” > click on Import > choose your pfx file

On the step below, I have used the code : 1234 used during export of pkcs12 (nejifromcompanyA.pfx) file

Browser challenging for export code before adding certificate
Browser challenging for export code before adding certificate
Browser showing successful notification after client added
Browser showing successful notification after client added
Browser listing current client certificate added
Browser listing current client certificate added

With that we make a call to the browser and it selects the needed client certificate and all we need to do is to confirm.

Browswer prompting for certificate to select
Browswer prompting for certificate to select

cURL

Let’s now select the client’s certificate to make call with.

JVM client

I have fallen in love with groovy for a while now and I will rather use groovy to make a non exhaustive client taking advantage of the scripting side of the language. Here again, we need to do a couple of concerns. Java by nature will throw an exception of the type “javax.net.ssl.SSLPeerUnverifiedException: peer not authenticated” if the server ssl is self signed or has expired. We will take care of that 😐

We need to tell our groovy client to chill and trust our jokes.mycodingpains.com.crt by creating a truststore and adding jokes server certificate to it.
The command below will create a truststore for us of the name jokesapp.jks and will add our jokes server certificate with some key and store password “jokes1234”

Now we will need to take care of our client certificate bit. For that, we will need to create a keystore this time. our keystore will contain both our nejifromcompanyA.crt and nejifromcompanyA.key. This is exactly what nejifromcompanyA.pfx represent. Remember we generated the pfx file for our browser to be able to authenticate with our jokes server. The pass phrase we used for the export of the pfx file is 1234. The command below will use the pfx and create a jks file out of it. I will ask for the export pass phrase of the pfx file as it’s source password and will ask for us to type in a new pass phrase as destination password. We will use neji1234 for the destination password

Let’s do a routine verification of our truststore and keystore just to be on the safer side.

It looks like it’s all nice and clean, let’s create our groovy client then.

Notice this script is in the same directory as all the certificate files.

Calling our server

That’s all folks, I hope this saves you some time and from some heavy google-fu and banging of head. This post was influenced by Nate Good’s post But there were a lot reading and head banging especially during some integration with partners from South Africa. Maybe will invest some time in making this work with java straight.

Leave a Reply

Your email address will not be published. Required fields are marked *

captcha * Time limit is exhausted. Please reload the CAPTCHA.

This site uses Akismet to reduce spam. Learn how your comment data is processed.

Scroll to top