diff options
Diffstat (limited to '')
-rw-r--r-- | doc/examples/ex-ocsp-client.c | 321 |
1 files changed, 321 insertions, 0 deletions
diff --git a/doc/examples/ex-ocsp-client.c b/doc/examples/ex-ocsp-client.c new file mode 100644 index 0000000..d675e77 --- /dev/null +++ b/doc/examples/ex-ocsp-client.c @@ -0,0 +1,321 @@ +/* This example code is placed in the public domain. */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <gnutls/gnutls.h> +#include <gnutls/crypto.h> +#include <gnutls/ocsp.h> +#ifndef NO_LIBCURL +#include <curl/curl.h> +#endif +#include "read-file.h" + +size_t get_data(void *buffer, size_t size, size_t nmemb, void *userp); +static gnutls_x509_crt_t load_cert(const char *cert_file); +static void _response_info(const gnutls_datum_t * data); +static void +_generate_request(gnutls_datum_t * rdata, gnutls_x509_crt_t cert, + gnutls_x509_crt_t issuer, gnutls_datum_t *nonce); +static int +_verify_response(gnutls_datum_t * data, gnutls_x509_crt_t cert, + gnutls_x509_crt_t signer, gnutls_datum_t *nonce); + +/* This program queries an OCSP server. + It expects three files. argv[1] containing the certificate to + be checked, argv[2] holding the issuer for this certificate, + and argv[3] holding a trusted certificate to verify OCSP's response. + argv[4] is optional and should hold the server host name. + + For simplicity the libcurl library is used. + */ + +int main(int argc, char *argv[]) +{ + gnutls_datum_t ud, tmp; + int ret; + gnutls_datum_t req; + gnutls_x509_crt_t cert, issuer, signer; +#ifndef NO_LIBCURL + CURL *handle; + struct curl_slist *headers = NULL; +#endif + int v, seq; + const char *cert_file = argv[1]; + const char *issuer_file = argv[2]; + const char *signer_file = argv[3]; + char *hostname = NULL; + unsigned char noncebuf[23]; + gnutls_datum_t nonce = { noncebuf, sizeof(noncebuf) }; + + gnutls_global_init(); + + if (argc > 4) + hostname = argv[4]; + + ret = gnutls_rnd(GNUTLS_RND_NONCE, nonce.data, nonce.size); + if (ret < 0) + exit(1); + + cert = load_cert(cert_file); + issuer = load_cert(issuer_file); + signer = load_cert(signer_file); + + if (hostname == NULL) { + + for (seq = 0;; seq++) { + ret = + gnutls_x509_crt_get_authority_info_access(cert, + seq, + GNUTLS_IA_OCSP_URI, + &tmp, + NULL); + if (ret == GNUTLS_E_UNKNOWN_ALGORITHM) + continue; + if (ret == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) { + fprintf(stderr, + "No URI was found in the certificate.\n"); + exit(1); + } + if (ret < 0) { + fprintf(stderr, "error: %s\n", + gnutls_strerror(ret)); + exit(1); + } + + printf("CA issuers URI: %.*s\n", tmp.size, + tmp.data); + + hostname = malloc(tmp.size + 1); + if (!hostname) { + fprintf(stderr, "error: cannot allocate memory\n"); + exit(1); + } + memcpy(hostname, tmp.data, tmp.size); + hostname[tmp.size] = 0; + + gnutls_free(tmp.data); + break; + } + + } + + /* Note that the OCSP servers hostname might be available + * using gnutls_x509_crt_get_authority_info_access() in the issuer's + * certificate */ + + memset(&ud, 0, sizeof(ud)); + fprintf(stderr, "Connecting to %s\n", hostname); + + _generate_request(&req, cert, issuer, &nonce); + +#ifndef NO_LIBCURL + curl_global_init(CURL_GLOBAL_ALL); + + handle = curl_easy_init(); + if (handle == NULL) + exit(1); + + headers = + curl_slist_append(headers, + "Content-Type: application/ocsp-request"); + + curl_easy_setopt(handle, CURLOPT_HTTPHEADER, headers); + curl_easy_setopt(handle, CURLOPT_POSTFIELDS, (void *) req.data); + curl_easy_setopt(handle, CURLOPT_POSTFIELDSIZE, req.size); + curl_easy_setopt(handle, CURLOPT_URL, hostname); + curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, get_data); + curl_easy_setopt(handle, CURLOPT_WRITEDATA, &ud); + + ret = curl_easy_perform(handle); + if (ret != 0) { + fprintf(stderr, "curl[%d] error %d\n", __LINE__, ret); + exit(1); + } + + curl_easy_cleanup(handle); +#endif + + _response_info(&ud); + + v = _verify_response(&ud, cert, signer, &nonce); + + gnutls_x509_crt_deinit(cert); + gnutls_x509_crt_deinit(issuer); + gnutls_x509_crt_deinit(signer); + gnutls_global_deinit(); + + return v; +} + +static void _response_info(const gnutls_datum_t * data) +{ + gnutls_ocsp_resp_t resp; + int ret; + gnutls_datum buf; + + ret = gnutls_ocsp_resp_init(&resp); + if (ret < 0) + exit(1); + + ret = gnutls_ocsp_resp_import(resp, data); + if (ret < 0) + exit(1); + + ret = gnutls_ocsp_resp_print(resp, GNUTLS_OCSP_PRINT_FULL, &buf); + if (ret != 0) + exit(1); + + printf("%.*s", buf.size, buf.data); + gnutls_free(buf.data); + + gnutls_ocsp_resp_deinit(resp); +} + +static gnutls_x509_crt_t load_cert(const char *cert_file) +{ + gnutls_x509_crt_t crt; + int ret; + gnutls_datum_t data; + size_t size; + + ret = gnutls_x509_crt_init(&crt); + if (ret < 0) + exit(1); + + data.data = (void *) read_file(cert_file, RF_BINARY, &size); + data.size = size; + + if (!data.data) { + fprintf(stderr, "Cannot open file: %s\n", cert_file); + exit(1); + } + + ret = gnutls_x509_crt_import(crt, &data, GNUTLS_X509_FMT_PEM); + free(data.data); + if (ret < 0) { + fprintf(stderr, "Cannot import certificate in %s: %s\n", + cert_file, gnutls_strerror(ret)); + exit(1); + } + + return crt; +} + +static void +_generate_request(gnutls_datum_t * rdata, gnutls_x509_crt_t cert, + gnutls_x509_crt_t issuer, gnutls_datum_t *nonce) +{ + gnutls_ocsp_req_t req; + int ret; + + ret = gnutls_ocsp_req_init(&req); + if (ret < 0) + exit(1); + + ret = gnutls_ocsp_req_add_cert(req, GNUTLS_DIG_SHA1, issuer, cert); + if (ret < 0) + exit(1); + + + ret = gnutls_ocsp_req_set_nonce(req, 0, nonce); + if (ret < 0) + exit(1); + + ret = gnutls_ocsp_req_export(req, rdata); + if (ret != 0) + exit(1); + + gnutls_ocsp_req_deinit(req); + + return; +} + +static int +_verify_response(gnutls_datum_t * data, gnutls_x509_crt_t cert, + gnutls_x509_crt_t signer, gnutls_datum_t *nonce) +{ + gnutls_ocsp_resp_t resp; + int ret; + unsigned verify; + gnutls_datum_t rnonce; + + ret = gnutls_ocsp_resp_init(&resp); + if (ret < 0) + exit(1); + + ret = gnutls_ocsp_resp_import(resp, data); + if (ret < 0) + exit(1); + + ret = gnutls_ocsp_resp_check_crt(resp, 0, cert); + if (ret < 0) + exit(1); + + ret = gnutls_ocsp_resp_get_nonce(resp, NULL, &rnonce); + if (ret < 0) + exit(1); + + if (rnonce.size != nonce->size || memcmp(nonce->data, rnonce.data, + nonce->size) != 0) { + exit(1); + } + + ret = gnutls_ocsp_resp_verify_direct(resp, signer, &verify, 0); + if (ret < 0) + exit(1); + + printf("Verifying OCSP Response: "); + if (verify == 0) + printf("Verification success!\n"); + else + printf("Verification error!\n"); + + if (verify & GNUTLS_OCSP_VERIFY_SIGNER_NOT_FOUND) + printf("Signer cert not found\n"); + + if (verify & GNUTLS_OCSP_VERIFY_SIGNER_KEYUSAGE_ERROR) + printf("Signer cert keyusage error\n"); + + if (verify & GNUTLS_OCSP_VERIFY_UNTRUSTED_SIGNER) + printf("Signer cert is not trusted\n"); + + if (verify & GNUTLS_OCSP_VERIFY_INSECURE_ALGORITHM) + printf("Insecure algorithm\n"); + + if (verify & GNUTLS_OCSP_VERIFY_SIGNATURE_FAILURE) + printf("Signature failure\n"); + + if (verify & GNUTLS_OCSP_VERIFY_CERT_NOT_ACTIVATED) + printf("Signer cert not yet activated\n"); + + if (verify & GNUTLS_OCSP_VERIFY_CERT_EXPIRED) + printf("Signer cert expired\n"); + + gnutls_free(rnonce.data); + gnutls_ocsp_resp_deinit(resp); + + return verify; +} + +size_t get_data(void *buffer, size_t size, size_t nmemb, void *userp) +{ + gnutls_datum_t *ud = userp; + + size *= nmemb; + + ud->data = realloc(ud->data, size + ud->size); + if (ud->data == NULL) { + fprintf(stderr, "Not enough memory for the request\n"); + exit(1); + } + + memcpy(&ud->data[ud->size], buffer, size); + ud->size += size; + + return size; +} |