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?
Okay, I'm an idiot :-)
The answer is that OpenSSL has a bunch of macros that generate generic functions for converting something from struct
s 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.