Search code examples
clibcurllibcrypto

OCSP with libcrypto and libcurl


I'm writing an application which needs to verify the validity of certificates. For that I'd like to use OCSP. Rather than implementing OCSP myself, I was looking at OpenSSL's libcrypto to prepare the necessary data.

I came up with this (error handling removed for clarity):

int main(int argc, char** argv) {
    SSL_library_init();
    FILE *cert_f = fopen(argv[1], "r");
    FILE *cacert_f = fopen(argv[2], "r");
    X509 *cert = d2i_X509_fp(cert_f, NULL);
    X509 *cacert = d2i_X509_fp(cacert_f, NULL);
    fclose(cert_f);
    fclose(cacert_f);

    OCSP_REQUEST *req = OCSP_REQUEST_new();
    OCSP_CERTID *id = OCSP_cert_to_id(EVP_sha1(), cert, cacert);
    OCSP_request_add0_id(req, id);
    BIO *bio = BIO_new_connect("ocspserver:http");
    OCSP_RESPONSE *resp = OCSP_sendreq_bio(bio, "/2", req);
}

Which sends out the request just fine. Only the problem is that OCSP_sendreq_bio() does not appear to support sending HTTP requests via an HTTP proxy, which is a blocker for me. The documentation has the following to say about that:

These functions only perform a minimal HTTP query to a responder. If an application wishes to support more advanced features it should use an alternative more complete HTTP library.

Only it doesn't say how to do so, and I can't seem to figure it out. The OCSP_sendreq_bio() call does the conversion of the OCSP_REQUEST struct to an ASN.1 structure, but there don't appear to be any public calls that allow me to do so (unless I'm missing something).

Am I going about this the right way? If not, what should I be doing instead?


Solution

  • Okay, I'm an idiot :-)

    The answer is that OpenSSL has a bunch of macros that generate generic functions for converting something from structs to the ASN.1 binary format. These all have the form i2d_STRUCT_NAME (and d2i_STRUCT_NAME for the reverse operation). The exact i2d_OCSP_REQUEST and d2i_OCSP_RESPONSE functions aren't documented, but some others (e.g., d2i_X509, which I'm friggin' using in my question) are and have a very similar function signature (since they're generated with the same macro).

    The answer is thus to replace OCSP_sendreq_bio with something like this:

    unsigned char *data = NULL;
    long len = (long)i2d_OCSP_REQUEST(req, &data);
    CURL *curl = curl_easy_init();
    curl_easy_setopt(curl, CURLOPT_URL, "http://ocspserver/2");
    curl_easy_setopt(curl, CURLOPT_POST, (long)1);
    curl_easy_setopt(curl, CURLOPT_POSTFIELDS, data);
    curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, len);
    curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, my_memory_append_function);
    curl_easy_perform(curl);
    

    etc.