Inter-Service Communication using Client Certificate Authentication

SSL - Security goldLets say we have a service like Google. We have a mail app and a social app. Both have the ability to add events to a schedule. We don’t want to have to go to the mail app to see the events added to the schedule from there and then switch to the social app to see the events added from there. Instead we have another schedule app that contains all our events from both apps.

This Service Oriented Architecture is great, but how do you verify that the client (requestor) has permission to access the service? In this case, that the email app has permission to create events on the schedule app.

Enter SSL.

SSL is used to ensure all data between the client and server are private. To ensure all the data is private, a server requires an SSL Certificate.

SSL Certificates have a key pair (a public and private key) and a subject, which is the identity of the certificate/website owner. Typically the subject of an SSL Certificate will contain the domain name, organization name, address, city, state and country. It will also contain the expiration date of the Certificate and details of the Certification Authority responsible for the issuance of the Certificate. (NOTE: Anything encrypted with the certificates private key can only be decrypted with the public key and vice versa. )

This certificate is also used to verify that the website is who they say they are.
The most common situation is a browser showing the lock icon which tells the user that the identity of the website is verified. Since an SSL Certificate can be used to verify the server is who they say they are, then we should be able to use the same approach to verify a client.

Typical Browser-Website SSL Flow.

Certificate Authority: Verisign, Godaddy, DigiCert, etc
Client: Browser
Service: Website

  • The Certificate Authority (CA) creates a certificate. This is THEIR certificate that is used to prove the identity of the CA.
  • The Service creates a CSR (Certificate Signing Request). The CSR will have details about the identity of the website and company.
  • The Service will then submit this CSR to the CA to get validated.
  • The CA will validate the details in the CSR.
  • The CA uses their certificate to digitally sign the CSR (Certificate Signing Request). This creates an SSL Certificate which is then issued by the service.

When the Client connects to the Service, it establishes an encrypted link using the SSL Certificate. The Client also tries to verify the identity of the Service. To do this, the Client gets the SSL Certificate from the Service and checks that it has not expired, is being used for the domain that it has been issued, and was issued by a Certification Authority the Client trusts (Verisign, Godaddy, DigiCert, etc). The Client keeps a list of all the certificates from CAs it trusts and compares those with the CA that comes down with the SSL Certificate. (Note: The Browser decides which Certificate Authorities it is going to trust but you can add your own to the browser too. This is how you get your browser to trust a self-signed certificate, for example.)

Instead of the Client wanting to verify the identity of the Service, we want to do the reverse.

Note Using the initial example, the Client would be the mail/social app and the Service would be the Schedule app.

Setup SSL Authentication

First thing we need to do is create a Certificate Authority (We are our own Certificate Authority now). This will be used to sign the Client CSR. Since we are reversing everything, the client issues the CSR instead of the Service.

Here’s how to generate a private key and a certificate request, followed by self-signing the certificate.

openssl genrsa -out ca.key 1024
openssl req -new -key ca.key -out ca.csr
openssl x509 -req -days 365 -in ca.csr -signkey ca.key -out ca.crt

You will then need to copy the ca.crt file to your Service. This is our list of trusted CAs, consisting of just us.

If using apache you will need to set options in httpd.conf file:


...
SSLCACertificateFile /path/to/cacert/ca.crt
SSLVerifyClient require
...

Check out apache’s site for more information on other options.

For NGINX, add the following to the nginx.conf file:

http {
    server {
      ……...

     ssl_client_certificate /path/to/cacert/ca.crt;
     ssl_verify_client    on;

     ……..
   }
}

Check out nginx’s site for more options.

Each Client will need its own SSL Certificate. The Client will create a CSR and send a request to the Certificate Authority (which is us).

Generate the private key (in this case client.key) and a certificate request for the Client

openssl genrsa -out client.key 1024
openssl req -new -key client.key -out client.csr

Then sign the request using the Certificate Authority certificate and key we created (ca.crt, ca.key) and return the signed certificate.

openssl x509 -req -days 365 -CA ca.crt -CAkey ca.key -CAcreateserial -in client.csr -out client.crt

Now when the client makes a request to the service it should pass the client.crt

Going back to our first example. I’m going to make a request to add an event to the schedule from the mail app. I am going to use Faraday because it is awesome.

    request = Faraday::Connection.new https://schedule.com, :ssl => {
      :verify => false,
      :client_cert => OpenSSL::X509::Certificate.new(File.read(/path/to/mail_client.crt)),
      :client_key => OpenSSL::PKey::RSA.new(File.read(/path/to/mail_client.key)),
    }
    request.headers['Accept'] = 'application/json'
    request.headers['Content-Type'] = 'application/json'
    response = request.post "/events", {event: {start_time:"2013-08-01T16:20:00+00:00", end_time:"2013-08-01T18:20:00+00:00", name:"Meeting"}}.to_json

:verify in this case means that the Client is verifying that the Service is who they say they are (Typical Browser-Website Flow). If set to true you need to add the :ca_file => 'path/to/cafile' option as well. The ca_file in this case is the CA file used to create your domain’s (meaning, the Service) SSL certificate. If you bought your SSL Certificate from Godaddy you would need to download the CA Certificate they used to create your SSL Certificate. (They have a list here ).

Why this approach then instead of basic auth or oauth? The biggest thing is that the service doesn’t have to do anything when adding more clients. A new client would simply create a CSR and request an SSL Certificate from the same Certificate Authority.

Originally got the idea from this
Collective Idea article.

We are pretty early in our approach, but so far, it looks very promising.

Free book: Jump Start HTML5 Basics

Grab a free copy of one our latest ebooks! Packed with hints and tips on HTML5's most powerful new features.

  • http://bibwild.wordpress.com Jonathan Rochkind

    Wait, does this example really have the service verifying the client?

    You have the client verifying that the service is using a known cert, “the Client is verifying that the Service is who they say they are.”

    But you started out asking us “but how do you verify that the client (requestor) has permission to access the service?”

    Is there a part of this example that has the service verifying that the client (requestor) has permission to access the service, and I’m just not following it? I was excited for a simple example of that, but I think it’s not actually here?

  • http://bibwild.wordpress.com Jonathan Rochkind

    Ah, wait, I think I see, it’s the `SSLVerifyClient require` line?

    • Kevin Musselman

      Yeah, the service will need to set that option in apache and will use the SSLCACertificateFile option that you also set to verify the client.