"Manual" OCSP Certificate Validation Part 1: OpenSSL / Curl

"Manual" OCSP Certificate Validation Part 1: OpenSSL / Curl
Photo by Philipp Katzenberger / Unsplash

In any organization that relies on Public Key Infrastructure (PKI) for authentication of users and/or devices, one of the key pillars in supporting that infrastructure is the validation of certificates. The story goes like this: Your organization has gone through the effort of either setting up a Certificate Authority (CA) or using an external CA, and issuing certificates to all of your users and/or devices. Now in order to use those certificates for authentication you need a way to validate any certificate that is presented.

Validation will obviously involve checking the CA chain of the cert, the signature, and the dates when the cert is valid – all of which can be done using only the cert itself in combination with the CA certs. But what about the scenario where a user or device was originally issued a perfectly good cert, but that cert has to be revoked, either because the user/device has left the organization or has been deemed untrustworthy for whatever reason? In such a scenario there is nothing inherently wrong with the cert by itself--after all, it was issued by a proper CA and the dates are valid etc.

Therefore any organization relying on PKI needs a way to check if a cert has been revoked using some system beyond the certificate itself. There are two main options for how this is done: Using Certificate Revocation Lists (CRLs) or using Online Certificate Status Protocol (OCSP). Using CRLs involves downloading a potentially extremely long list of all certificates that have been revoked in the organization, and then checking the current cert against that list. In practice, that process can take a long time, and so for any system that is concerned with the user experience, OCSP is the way to go.

In a nutshell, OCSP validation involves calling an API endpoint (maintained by the CA) with the cert. The API responds saying whether the cert is still good or not. And that's that--super fast and super sweet.

In this blog post, we're going to show how to do OCSP validation on a cert "manually" with curl.

Calling OCSP With OpenSSL

Before looking at how to use curl to do OCSP validation, let's see how it can be done on the command line with OpenSSL to get a better understanding of the process.

First, we want to know the OCSP URL to call to validate our cert. The CA should be able to provide this info, but it is also included in the cert itself. The following OpenSSL command gives us the URL:

openssl x509 -in mycert.cer -ocsp_uri -noout

What this means:
openssl x509 This signals we're using an OpenSSL command to parse the X509 certificate.
 -in mycert.cer The certificate to read the OCSP URL from.
 -ocsp_uri The flag telling OpenSSL we want it to give us the OCSP URL from the cert.
 -noout This tells OpenSSL not to print out the (base64-encoded) cert to the screen.

In a typical scenario, you will be dealing with certs that have all been issued by the same CA and so they should all have the same OCSP URL. So you would only need to determine that URL once. But if you deal with certs that were issued by different CA's, or if the CA changed its OCSP URL then you might have to do that check for every cert.

Now that we have the OCSP URL we are ready to make the OCSP call:

openssl ocsp -issuer cacert.cer -cert mycert.cer -url https://ocsp.my.ca.com -text [-no_nonce]

This command will call the OCSP endpoint to validate the cert and display the response in a human-readable format. Although the command is fairly transparent, let's break down each part:

openssl ocsp This signals we are using an OpenSSL OCSP command.

-issuer cacert.cer The certificate of the issuing Certificate Authority.

-cert mycert.cer The certificate that we will validate against OCSP.

-url https://ocsp.my.ca.com The OCSP endpoint.

-text This tells OpenSSL to display the response in a human-readable text format.

-no_nonce By default, OpenSSL will send a "nonce" to the OCSP endpoint. This is a random string that the endpoint should return, as an extra validation that the response comes from the server we sent the request to. Adding -no_nonce signals not to send that. Whether nonce is necessary is up to your CA.

The response should look something like this, listing first the OCSP Request data, then the OCSP Response data with some info about the cert:

OCSP Request Data:
    Version: 1 (0x0)
    Requestor List:
    	Certificate ID:
        	Hash Algorithm: sha1
            Issuer Name Hash: ...
            ...
OCSP Response Data:
    OCSP Response Status: successful (0x0)
    Response Type: Basic OCSP Response
    Version: 1 (0x0)
    Responder Id: ...
    Produced At: ...
    Responses:
    Certificate ID:
      Hash Algorithm: sha1
      Issuer Name Hash: ...
      ....

If that's all there is, then the certificate is good. A bad response will include something like this:

Responder Error: unauthorized (6)

Now the above is all fine if we want OpenSSL to do the whole shebang from beginning to end. But we can break that up a little bit to see what's going on. Let's consider what OpenSSL is doing for us:

  • Creates OCSP request data
  • Sends that data to the OCSP endpoint
  • Receives the response and converts it to human-readable format

Let's remove OpenSSL from the second of those steps--sending the request to the OCSP endpoint. After all, we have other tools that we can use to call web endpoints, like curl. We just need OpenSSL to do the first (to generate the data to be used in the request) and the third (to interpret the response). Here's how we can do that:

Here's how OpenSSL can just generate the OCSP request and save it in a file:

openssl ocsp -cert mycert.cer -issuer cacert.cer -url https://ocsp.my.ca.com [no_nonce] -reqout ocsp_req.req

All we did was swap out -text and swap in -reqout ocsp_req.req. And--you guessed it--that will cause OpenSSL to write the request to a file ocsp_req.req.

If you open up ocsp_req.req you'll see that it's binary data. Since this is going to be used in a web request and since binary data can often get messed up when sent over the web (due to character encoding changes), we're actually going to want a Base64 encoded version of the binary data. We can convert our binary data toBase64 using another OpenSSL command:

openssl base64 -e -in ocsp_req.req -out ocsp_req.req.b64

openssl base64 Base64 operations

-e encrypt the file specified with -in and write it to the -out file

The contents of the Base64-encoded request look something like this:

MEUwQz....c=

Using that we can call the OCSP endpoint directly with curl. All we need to do is append the Base64-encoded string to the OCSP URL and call that endpoint:

curl https://ocsp.my.ca.com/MEUwQz....c= -o ocsp_response.res

Now to see the response info in human-readable format:

openssl ocsp -respin ocsp_response.res -text

That gives the same info as seen above (OCSP Response Data: etc). And that concludes how we can call OCSP using OpenSSL to generate the request / read the response, and Curl to actually call the OCSP endpoint.

In our next blog post, we will do the same thing in Java!