[ Team LiB ] |
10.12 Checking Revocation Status via OCSP with OpenSSL10.12.1 ProblemYou have a certificate that you want to verify, as well as the certificate used to issue it (and any others that may be in the certification path), but you need to check that the certificates have not been revoked. One way to do this is to download the CRL from the issuing CA, but an alternative is to check an OCSP responder for an immediate response. Using OCSP allows you to avoid the overhead of downloading a potentially very large CRL file. 10.12.2 SolutionMost CAs publish CRLs, but most do not run OCSP responders. A number of public OCSP responders collect CRLs from a number of different CAs and are capable of responding for each of them. Such responders are known as chain responders, and they should only be trusted if their certificate can be verified or if it is trusted and it contains the extKeyUsage extension with the OCSPSigning bit enabled. A reasonably up-to-date list of these public responders is available from http://www.openvalidation.org. For those CAs that run their own OCSP responders, it's best to contact them directly rather than relying on a chain responder, because the information from a CA's responder is more likely to be the most up-to-date. In Recipe 10.10, we built a lookup table of various CAs that contains information about where their CRLs can be found. You will notice that OCSP responder information is also present for those CAs that have their own. At the time of this writing, the only CA that has its own responder (so far as we have been able to determine) is VeriSign. 10.12.3 DiscussionChecking a certificate's revocation status using an OCSP responder requires three things: the address of the OCSP responder, the certificate to be checked, and the certificate that issued the certificate you want to check. With these three items, OpenSSL makes quick work of communicating with an OCSP responder. A number of tunable variables that affect the verification process are supported, so we have created a data structure to hold this information: #include <openssl/ocsp.h> #include <openssl/ssl.h> typedef struct { char *url; X509 *cert; X509 *issuer; spc_x509store_t *store; X509 *sign_cert; EVP_PKEY *sign_key; long skew; long maxage; } spc_ocsprequest_t; The fields in this structure are as follows:
Querying an OCSP responder is actually a complex operation, even though we are effectively reducing the amount of work necessary for you to a single function call. Because of the complexity of the operation, a number of things can go wrong, and so we have defined a sizable number of possible error codes. In some cases, we have lumped a number of finer-grained errors into a single error code, but the code presented here can easily be expanded to provide more detailed error information. typedef enum { SPC_OCSPRESULT_ERROR_INVALIDRESPONSE = -12, SPC_OCSPRESULT_ERROR_CONNECTFAILURE = -11, SPC_OCSPRESULT_ERROR_SIGNFAILURE = -10, SPC_OCSPRESULT_ERROR_BADOCSPADDRESS = -9, SPC_OCSPRESULT_ERROR_OUTOFMEMORY = -8, SPC_OCSPRESULT_ERROR_UNKNOWN = -7, SPC_OCSPRESULT_ERROR_UNAUTHORIZED = -6, SPC_OCSPRESULT_ERROR_SIGREQUIRED = -5, SPC_OCSPRESULT_ERROR_TRYLATER = -3, SPC_OCSPRESULT_ERROR_INTERNALERROR = -2, SPC_OCSPRESULT_ERROR_MALFORMEDREQUEST = -1, SPC_OCSPRESULT_CERTIFICATE_VALID = 0, SPC_OCSPRESULT_CERTIFICATE_REVOKED = 1 } spc_ocspresult_t; You will notice that any nonzero result code is an error of some kind—whether it is an error resulting in a failure to obtain the revocation status of the certificate in question, or one indicating that the certificate has been revoked. When checking the error codes, do not assume that zero means failure, as is the norm. You should always use these constants, instead of simple boolean tests, when checking the result of an OCSP operation. The following result codes have special meaning:
Once an spc_ocsprequest_t structure is created and appropriately initialized, communicating with the OCSP responder is a simple matter of calling spc_verify_via_ocsp( ) and checking the result code. spc_ocspresult_t spc_verify_via_ocsp(spc_ocsprequest_t *data) { BIO *bio = 0; int rc, reason, ssl, status; char *host = 0, *path = 0, *port = 0; SSL_CTX *ctx = 0; X509_STORE *store = 0; OCSP_CERTID *id; OCSP_REQUEST *req = 0; OCSP_RESPONSE *resp = 0; OCSP_BASICRESP *basic = 0; spc_ocspresult_t result; ASN1_GENERALIZEDTIME *producedAt, *thisUpdate, *nextUpdate; result = SPC_OCSPRESULT_ERROR_UNKNOWN; if (!OCSP_parse_url(data->url, &host, &port, &path, &ssl)) { result = SPC_OCSPRESULT_ERROR_BADOCSPADDRESS; goto end; } if (!(req = OCSP_REQUEST_new( ))) { result = SPC_OCSPRESULT_ERROR_OUTOFMEMORY; goto end; } id = OCSP_cert_to_id(0, data->cert, data->issuer); if (!id || !OCSP_request_add0_id(req, id)) goto end; OCSP_request_add1_nonce(req, 0, -1); /* sign the request */ if (data->sign_cert && data->sign_key && !OCSP_request_sign(req, data->sign_cert, data->sign_key, EVP_sha1( ), 0, 0)) { result = SPC_OCSPRESULT_ERROR_SIGNFAILURE; goto end; } /* establish a connection to the OCSP responder */ if (!(bio = spc_connect(host, atoi(port), ssl, data->store, &ctx))) { result = SPC_OCSPRESULT_ERROR_CONNECTFAILURE; goto end; } /* send the request and get a response */ resp = OCSP_sendreq_bio(bio, path, req); if ((rc = OCSP_response_status(resp)) != OCSP_RESPONSE_STATUS_SUCCESSFUL) { switch (rc) { case OCSP_RESPONSE_STATUS_MALFORMEDREQUEST: result = SPC_OCSPRESULT_ERROR_MALFORMEDREQUEST; break; case OCSP_RESPONSE_STATUS_INTERNALERROR: result = SPC_OCSPRESULT_ERROR_INTERNALERROR; break; case OCSP_RESPONSE_STATUS_TRYLATER: result = SPC_OCSPRESULT_ERROR_TRYLATER; break; case OCSP_RESPONSE_STATUS_SIGREQUIRED: result = SPC_OCSPRESULT_ERROR_SIGREQUIRED; break; case OCSP_RESPONSE_STATUS_UNAUTHORIZED: result = SPC_OCSPRESULT_ERROR_UNAUTHORIZED; break; } goto end; } /* verify the response */ result = SPC_OCSPRESULT_ERROR_INVALIDRESPONSE; if (!(basic = OCSP_response_get1_basic(resp))) goto end; if (OCSP_check_nonce(req, basic) <= 0) goto end; if (data->store && !(store = spc_create_x509store(data->store))) goto end; if ((rc = OCSP_basic_verify(basic, 0, store, 0)) <= 0) goto end; if (!OCSP_resp_find_status(basic, id, &status, &reason, &producedAt, &thisUpdate, &nextUpdate)) goto end; if (!OCSP_check_validity(thisUpdate, nextUpdate, data->skew, data->maxage)) goto end; /* All done. Set the return code based on the status from the response. */ if (status = = V_OCSP_CERTSTATUS_REVOKED) result = SPC_OCSPRESULT_CERTIFICATE_REVOKED; else result = SPC_OCSPRESULT_CERTIFICATE_VALID; end: if (bio) BIO_free_all(bio); if (host) OPENSSL_free(host); if (port) OPENSSL_free(port); if (path) OPENSSL_free(path); if (req) OCSP_REQUEST_free(req); if (resp) OCSP_RESPONSE_free(resp); if (basic) OCSP_BASICRESP_free(basic); if (ctx) SSL_CTX_free(ctx); if (store) X509_STORE_free(store); return result; } 10.12.4 See Also
|
[ Team LiB ] |