Skip to content
 

OCSP verification with OpenSSL

OCSP (Online Certificate Status Protocol) is a protocol designed to perform online (ie, over the network) validity verification of X.509 certificates (as opposed to CRL - Certificate Revocation Lists -, which performs the checking against a local list of revoked certificates). OCSP exchanges ASN.1 encoded messages, usually (but not necessarily) over HTTP (in DER format).

Most popular browsers support it and can be configured to perform automatic OCSP verification of SSL certificates received from the sites the user visits. Firefox for example is configured to perform OCSP verification if the certificate specifies the URL of an OCSP server to query.

Now it turns out that sometimes it could be useful to perform OCSP queries from the command line or in scripts. A prime example of this is in OpenVPN; OpenVPN has no built-in support for OCSP, but it does offer hooks where user-defined scripts can be run. One such hook is defined using the tls-verify configuration directive, and that is just the right place to perform the query.

OpenSSL has, among its many command line functions, an OCSP utility, that can be used to perform OCSP queries and also to set up a simple OCSP server.

As an example, we will use it to perform OCSP validation for an SSL certificate presented by a secure website, namely https://shipit.ubuntu.com, which at the time of this writing is signed by GoDaddy. The certificate is a chained certificate, meaning that there are several levels of trust in the certificate chain, and thus several certificates sent from the server to the client. We can verify them all using OCSP in our test.

Certificate download

To download and save the certificates, we can use OpenSSL's s_client utility plus some scripting tricks:

$ openssl s_client -showcerts -connect shipit.ubuntu.com:443 < /dev/null | \
awk -v c=-1 '/-----BEGIN CERTIFICATE-----/{inc=1;c++} 
             inc {print > ("level" c ".crt")}
             /---END CERTIFICATE-----/{inc=0}'

This leaves us with a number of certificates in the current directory, named "level0.crt", "level1.crt" etc., where the first one is the actual server's certificate, and the following ones are the chain of CA certificates up the chain of trust. Let's check that this is indeed the case:

$ for i in level?.crt; do openssl x509 -noout -serial -subject -issuer -in "$i"; echo; done
serial=6C5877CE031D
subject= /O=*.ubuntu.com/OU=Domain Control Validated/CN=*.ubuntu.com
issuer= /C=US/ST=Arizona/L=Scottsdale/O=GoDaddy.com, Inc./OU=http://certificates.godaddy.com/repository/CN=Go Daddy Secure Certification Authority/serialNumber=07969287

serial=0301
subject= /C=US/ST=Arizona/L=Scottsdale/O=GoDaddy.com, Inc./OU=http://certificates.godaddy.com/repository/CN=Go Daddy Secure Certification Authority/serialNumber=07969287
issuer= /C=US/O=The Go Daddy Group, Inc./OU=Go Daddy Class 2 Certification Authority

serial=010D
subject= /C=US/O=The Go Daddy Group, Inc./OU=Go Daddy Class 2 Certification Authority
issuer= /L=ValiCert Validation Network/O=ValiCert, Inc./OU=ValiCert Class 2 Policy Validation Authority/CN=http://www.valicert.com//emailAddress=info@valicert.com

OCSP validation

Now to perform OCSP validation of a certificate, we need to have the certificate of the CA that signed it. For our certificate chain, we already have what we need except for the last certificate which, as we can see above, is not self-signed. Fortunately, the issuer certificate is included in Firefox's built-in trusted certificate list, so all we need to do is export it and save it as "level3.crt" (make sure you choose the right one: for our example, it should be the "Valicert Class 2 Policy Validation Authority" - Firefox also includes the certificates for "Class 1" and "Class 3"). A quick check also reveals that this certificate is self-signed, so we don't need to go further up in the chain:

$ openssl x509 -noout -serial -subject -issuer -in level3.crt
serial=01
subject= /L=ValiCert Validation Network/O=ValiCert, Inc./OU=ValiCert Class 2 Policy Validation Authority/CN=http://www.valicert.com//emailAddress=info@valicert.com
issuer= /L=ValiCert Validation Network/O=ValiCert, Inc./OU=ValiCert Class 2 Policy Validation Authority/CN=http://www.valicert.com//emailAddress=info@valicert.com

The next thing we need is the URL of the OCSP server where the certificate can be verified. Usually, this is included in the certificate itself (in the "Authority Information Access" X.509v3 extension); if not, it's usually listed on the CA's web page (and if not, something like "ocsp.CAname.com" usually works). So let's see:

$ for i in level?.crt; do echo "$i:"; openssl x509 -noout -text -in "$i" | grep OCSP; done
level0.crt:
                OCSP - URI:http://ocsp.godaddy.com/
level1.crt:
                OCSP - URI:http://ocsp.godaddy.com
level2.crt:
                OCSP - URI:http://ocsp.godaddy.com
level3.crt:

The third URL is a bit unexpected, because that certificate was not issued by GoDaddy. So it seems that GoDaddy manages OCSP for other CAs. We will use the same URL for the verification of all the certificates.

The last thing we need is a set of trusted certificates. The response from the OCSP server is usually signed, and we need a set of certificates that we trust so OpenSSL can verify the response against one of these. We can use OpenSSL's default certificates (can be found under /etc/ssl/certs/ca-certificates.crt in most systems), plus our three CA certificates in the chain trust that we have previously determined:

$ cat /etc/ssl/certs/ca-certificates.crt level{1,2,3}.crt > CAbundle.crt

Note that ca-certificates.crt may already include one or more of our three certificates, but adding them twice does no harm.

We are thus ready to validate. OpenSSL "ocsp" utility accepts either a serial number or an actual certificate file (from which it extracts the serial number) as the specification of what to validate. The trusted certificates are specified using the -CAfile option, and the OCSP URL is specified with the -url option. So here's a basic verification for the "level0.crt" certificate:

$ l0serial=$(openssl x509 -serial -noout -in level0.crt); l0serial=${l0serial#*=}
$ openssl ocsp -issuer level1.crt -nonce -CAfile CAbundle.crt -url http://ocsp.godaddy.com/ -serial "0x${l0serial}"
WARNING: no nonce in response
Response verify OK
0x6C5877CE031D: good
        This Update: Apr 27 11:45:00 2010 GMT
        Next Update: Apr 27 18:13:42 2010 GMT

This looks good, except for the "no nonce" warning, which means that the response does not include an optional random number that would theoretically make it safer (we did include one in the query though). But we can't really help with that, as it's the server's responsibility to include or not that number.

We can, in a similar way, verify all the other certificates we had in the chain:

$ l1serial=$(openssl x509 -serial -noout -in level1.crt); l1serial=${l1serial#*=}
$ openssl ocsp -issuer level2.crt -nonce -CAfile CAbundle.crt -url http://ocsp.godaddy.com/ -serial "0x${l1serial}"
WARNING: no nonce in response
Response verify OK
0x0301: good
        This Update: May 11 18:39:26 2009 GMT
        Next Update: Apr 27 20:04:37 2010 GMT
$ l2serial=$(openssl x509 -serial -noout -in level2.crt); l2serial=${l2serial#*=}
$ openssl ocsp -issuer level3.crt -nonce -CAfile CAbundle.crt -url http://ocsp.godaddy.com/ -serial "0x${l2serial}"
WARNING: no nonce in response
Response verify OK
0x010D: good
        This Update: May 11 18:10:18 2009 GMT
        Next Update: Apr 27 20:04:56 2010 GMT
$ l3serial=$(openssl x509 -serial -noout -in level3.crt); l3serial=${l3serial#*=}
$ openssl ocsp -issuer level3.crt -nonce -CAfile CAbundle.crt -url http://ocsp.godaddy.com/ -serial "0x${l3serial}"
WARNING: no nonce in response
Response verify OK
0x01: good
        This Update: May 11 18:10:18 2009 GMT
        Next Update: Apr 27 20:04:56 2010 GMT

Here it happens that all the queries are sent to the same server; generally speaking, that need not be the case, and you should use the right server as specified by the issuer CA.

To get more verbose output, including a full dump of the OCSP transaction, you can add the -text option to the validation command.

Simple OCSP server

If you run your own CA, you can use the administrative files used by the CA to keep track of the certificates it signed (especially the certificate index) to create a simple OCSP server that answers queries related to those certificates.

Usually the CA information is contained in a single directory, and by default the files have standard names. With a more-or-less standard setup, this is the command to start a listening OCSP server (responder) on port 3456:

$ openssl ocsp -index index.txt -CA CAcert.pem -rsigner CAcert.pem -rkey private/CAkey.pem -port 3456
Waiting for OCSP client connections...

"index.txt" is the index file that OpenSSL creates when the CA is set up, and updates every time the CA signs or revokes a certificate. The private key is specified so the server can sign its responses. It's possible to add the -text and -out options to dump the queries onto a file.

We can check that it works by querying it from a machine where the CA certificate is installed:

$ openssl ocsp -issuer CAcert.pem -nonce -CAfile CAcert.pem -url http://my.ocsp.server:3456 -serial "0x0f"
Response verify OK
0x0f: good
        This Update: Apr 27 18:04:30 2010 GMT

Now let's have the CA revoke a certificate:

$ openssl ca -revoke certs/0A.pem -keyfile private/CAkey.pem -cert CAcert.pem
Using configuration from /etc/ssl/openssl.cnf
Revoking Certificate 0A.
Data Base Updated

and confirm that the OCSP query now correctly reports that the certificate is revoked:

$ openssl ocsp -issuer CAcert.pem -nonce -CAfile CAcert.pem -url http://my.ocsp.server:3456 -serial "0x0a"
Response verify OK
0x0a: revoked
        This Update: Apr 27 18:37:11 2010 GMT
        Revocation Time: Apr 27 18:35:43 2010 GMT

Beware that the shell exit status of the above query, despite reporting the "revoked" status, is 0, so to test that the certificate was revoked in a script you should parse the output and look for a line that starts with the serial number and has ": good" after that.

Conclusion

OCSP queries with OpenSSL are an easy way to add OCSP support to any program that does not support OCSP directly but allows user-supplied command to be run.
For simple and low-volume use, you can also set up an OCSP responder that uses the CA index file to answer queries.

The full set of option are in the offical OpenSSL documentation page for OCSP.

As a final note, the tests show that OpenSSL does not seem to support the use of a proxy to perform OCSP queries. So, even if you have the canonical *_proxy environment variables set, they are ignored.

7 Comments

  1. Tmt says:

    You need to check also for presence of:

    Response verify OK

    A response that is signed by unknown signature will still have the

    0x0f: good

    line

  2. helmut says:

    Very helpful article, saved me a lot of time, thanks.
    Just a remark to the final note:
    openssl can handle a proxy server
    instead of -url http://ocspserver
    just use
    -host proxy:port -path http://ocspserver

  3. Nicolae says:

    Could you please tell me how can I generate the "index.txt" (-index index.txt)? It's only an empty text file?

    • waldner says:

      As I wrote, "index.txt" is the index file that OpenSSL creates when the CA is set up, and updates every time the CA signs or revokes a certificate. It contains the list of certificates signed or revoked by the CA.
      Its location is configured either on the command line, or in the openssl.cnf file (or equivalent):

      database = $dir/index.txt # index file.

      in the [ ca ] or [ CA_default ] sections. I believe that if you've just set up the CA, the file will be empty. As soon as you sign or revoke certificates, entries will be added by OpenSSL.

      Note that this assumes a more or less conventional CA setup using OpenSSL (eg, using CA.pl); if you are using a different setup, you have to adapt it to your configuration.

  4. David says:

    Does anyone know how can I check just the status of the certificate without the issuer (just with serial number)?

    Thanks!

    • waldner says:

      Hi David,

      you need to know the issuer, because many different CAs may have issued a certificate with serial number, say, 100. So you have to know the issuer to know who to ask for the certificate status.