OCSP Stapling - UlricE/pen GitHub Wiki

Https websites use certificates to prove that they are who they claim they are. The certificates are signed by trusted entities, called certificate authorities. Trusted by whom? The person using the browser most likely have no idea what the CA:s are or who works there. But the browser has a built-in list of CA:s that it trusts. It is at least a little bit of trust, so let’s say that a little trust is better than no trust.

The certificates are good for a year or more, after which time they need to be renewed. But what if the certificate needs to be revoked before its time is up?

Kludge upon kludge upon kludge

One such scheme is OCSP, “online certificate status protocol”, where the browser connects to the CA and asks whether the certificate is still valid. Unlike the certificate itself, which has a life span of years, the OCSP response has a much shorter life of hours.

One may think that if the certificate had a shorter life to begin with, none of this would be necessary. But let’s ignore that for now. Surely this fixes the problem with trusting certificates that shouldn’t be trusted?

Well, not quite. There are a bunch of problems with OCSP. For one, it requires that the browser is free to connect to the OCSP responder (the thingy on the CA:s web site that says the certificate is good). If the guys administrating the network haven’t opened the firewall to every potential OCSP responder (remember that list of “trusted” certificate authorities in the browser), OCSP doesn’t work.

By the way, care to guess what a browser does by default if the certificate validity can’t be verified? It doesn’t care. It just carries on as if the certificate were good.

There are more problems with OCSP: it creates a lot of traffic to the CA:s, traffic that they have to pay for, making operating a CA more expensive. And the queries disclose to the CA what web sites the user visits. And the queries are typically unencrypted, so anyone with a wiretap knows too. Let’s make a list.

  • The user visiting https://shadybusiness.com/ knows (we hope)
  • Shady Business knows
  • Shady Business’ CA knows
  • Every governmental agency on the way from the user to the CA knows

This is obviously totally broken. It is less secure than if Shady Business had used a self-signed certificate. It is less secure than if Shady Business had used plain http.

Pull out the Swingline

What if the web server, instead of relying on the browser connecting to the CA to check the certificate status, sent the OCSP response itself? The response would still need to be pulled from the CA, but by the certificate owner, and only every few hours. This solves the traffic problem for the CA, it solves the privacy issue, and it saves the networking guys a bit of firewall configuration.

So how does one implement this? Let’s say, in an SSL-terminating load balancer?

A clue

Rob Stradling proposed this approach for Nginx:

You’ve got me thinking…

In the Apache httpd implementation, mod_ssl contains code which regularly downloads and caches OCSP Responses in shared memory so that they’re ready to be Stapled.

It occurs to me that the same result could be achieved without having to put the downloading/caching code inside the webserver. A cronjob could run a shell script that…

  • runs the OpenSSL command-line tool to download the appropriate OCSP Responses, saving them to the hard disk.
  • signals the webserver to reload its config file.

With this cronjob-based approach, the webserver would only require a small amount of new code:

  • a new config directive for specifying the filename of an OCSP Response (or perhaps the code could just look for .ors: if present, use it for Stapling; if absent, Stapling is disabled).
  • code to read the OCSP Response file.
  • a call to SSL_CTX_set_tlsext_status_cb().
  • some sanity checks on the OCSP Response (e.g. is it still time-valid?)
  • a call to SSL_set_tlsext_status_ocsp_resp() to actually staple the OCSP Response.

This cronjob-based approach should perform better than Apache/mod_ssl’s approach, because it would avoid the use of a mutexed shared-memory cache.

Eminently, commendably simple. Let’s do it.

Frustration

ulric@debian:~/Git/pen$ man SSL_CTX_set_tlsext_status_cb
No manual entry for SSL_CTX_set_tlsext_status_cb
ulric@debian:~/Git/pen$ man SSL_set_tlsext_status_ocsp_resp
No manual entry for SSL_set_tlsext_status_ocsp_resp

This is actually the typical situation when you try to do anything with OpenSSL. The manpages, when they exist, document details but not the big picture, and when they don’t exist they don’t document anything.

Implementation

After an actually very small amount of trial and error, here is a simplified version of the callback that does the stapling:

static int ssl_stapling_cb(SSL *ssl, void *p)
{
    unsigned char *ocsp_resp_copy;

    if (ocsp_resp_file) {
        int f = open(ocsp_resp_file, O_RDONLY);
        ocsp_resp_len = read(f, ocsp_resp_data, OCSP_RESP_MAX);
        close(f);
        free(ocsp_resp_file);
        ocsp_resp_file = NULL;
    }
    ocsp_resp_copy = pen_malloc(ocsp_resp_len);
    memcpy(ocsp_resp_copy, ocsp_resp_data, ocsp_resp_len);
    SSL_set_tlsext_status_ocsp_resp(ssl, ocsp_resp_copy, ocsp_resp_len);
    return SSL_TLSEXT_ERR_OK;
}

The real code also has error checking.

Putting it together

We now have a way to use the OCSP response, once it has found its way into a file. But how did it get there?

First we need to find out what the OCSP responder URI is. Fortunately that information is embedded in the certificate itself:

openssl x509 -noout -ocsp_uri -in /etc/pen/pen.siag.nu.pem
http://ocsp2.globalsign.com/gsdomainvalsha2g2

And to get that into the file:

openssl ocsp -noverify -issuer /etc/pen/globalsign-intermediate.crt -cert /etc/pen/pen.siag.nu.pem -url http://ocsp2.globalsign.com/gsdomainvalsha2g2 -header Host ocsp2.globalsign.com -respout /etc/pen/pen.siag.nu.ocsp

Finally, to get pen to reload the file (it has a short life span, remember):

penctl /var/run/pen/https.ctl "ssl_ocsp_response /etc/pen/pen.siag.nu.ocsp"