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
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
$ 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
$ cat /etc/ssl/certs/ca-certificates.crt level{1,2,3}.crt > CAbundle.crt
Note that
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
$ 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
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
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
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
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
Thanks, that has to be considered if doing the verification programmatically.
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
Could you please tell me how can I generate the "index.txt" (-index index.txt)? It's only an empty text file?
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.
Does anyone know how can I check just the status of the certificate without the issuer (just with serial number)?
Thanks!
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.