10.5 Performing X.509 Certificate Verification with OpenSSL

10.5.1 Problem

You have an X.509 certificate and you want to verify its validity using OpenSSL.

10.5.2 Solution

OpenSSL represents an X.509 certificate using an X509 object. Another object, an X509_STORE, must be combined with the X509 object to be verified into an X509_STORE_CTX object. An X509_STORE object contains the certificates that OpenSSL will use to verify the certificate under scrutiny, as well as an optional CRL. The X509_STORE_CTX object simply combines the X509_STORE and X509 objects. The actual certificate verification is performed by calling X509_verify_cert( ) and passing it the X509_STORE_CTX object.

10.5.3 Discussion

Actually performing the certificate verification requires a significant amount of setup work. Much of the work should not really be necessary, but there are some issues with the current version of OpenSSL that need to be addressed. The OpenSSL team is aware of the problems we have encountered, and we anticipate that they will be fixed at some point in the future, but unfortunately, we do not know when that might be.

OpenSSL provides a set of functions for manipulating X509_STORE objects, and we will be using them, but in versions of OpenSSL up to and including the initial release of 0.9.7, no X.509 objects are reference counted while other OpenSSL objects (including EVP_PKEY, SSL_CTX, and many others) are. This presents a problem for us because much of the code that we will be presenting needs to have only a single X509_STORE object used for different purposes. If we attach the X509_STORE object to an SSL_CTX, for example, when the SSL_CTX is destroyed, so is the X509_STORE object. When trying to build a higher-level API on top of OpenSSL's API, things quickly get ugly.

The situation is complicated by the fact that OpenSSL provides no APIs to duplicate objects. Our solution to this problem as a whole is to create a new structure that contains everything we might need, then to create X509_STORE objects from that structure as we need them. It is obviously not optimal, and it is also not a perfect solution, but it is difficult to do any better. The proper solution is OpenSSL's to implement, but it's not a small task. Reference counting is often difficult to get right, and adding that kind of memory management into a large body of existing code is even harder.

We begin our solution by defining two data types. One is merely a convenience for a function pointer. The other is the core of our X509_STORE wrapper:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <openssl/evp.h>
#include <openssl/x509.h>
typedef int (*spc_x509verifycallback_t)(int, X509_STORE_CTX *);
typedef struct {
  char                      *cafile;
  char                      *capath;
  char                      *crlfile;
  spc_x509verifycallback_t  callback;
  STACK_OF(X509)            *certs;
  STACK_OF(X509_CRL)        *crls;
  char                      *use_certfile;
  STACK_OF(X509)            *use_certs;
  char                      *use_keyfile;
  EVP_PKEY                  *use_key;
  int                       flags;
} spc_x509store_t;

We will not get into any detailed explanation of this structure here. Instead, we will provide a complete set of functions to manipulate the structure and explain as we go along. The first two functions are used to initialize and clean up an spc_x509store_t object. The caller is responsible for allocating memory for the object as necessary. Our API will only manage the object's contents.

void spc_init_x509store(spc_x509store_t *spc_store) {
  spc_store->cafile       = 0;
  spc_store->capath       = 0;
  spc_store->crlfile      = 0;
  spc_store->callback     = 0;
  spc_store->certs        = sk_X509_new_null(  );
  spc_store->crls         = sk_X509_CRL_new_null(  );
  spc_store->use_certfile = 0;
  spc_store->use_certs    = sk_X509_new_null(  );
  spc_store->use_keyfile  = 0;
  spc_store->use_key      = 0;
  spc_store->flags        = 0;
void spc_cleanup_x509store(spc_x509store_t *spc_store) {
  if (spc_store->cafile)       free(spc_store->cafile);
  if (spc_store->capath)       free(spc_store->capath);
  if (spc_store->crlfile)      free(spc_store->crlfile);
  if (spc_store->use_certfile) free(spc_store->use_certfile);
  if (spc_store->use_keyfile)  free(spc_store->use_keyfile);
  if (spc_store->use_key)      EVP_PKEY_free(spc_store->use_key);

The next three functions are used to set the locations from which trusted certificates and certificate revocation lists will be loaded:

spc_x509store_setcafile( )

Accepts a filename that specifies a single file containing any number of PEM- encoded certificates. (See Recipe 7.17 for a discussion of PEM files.)

spc_x509store_setcapath( )

Accepts a pathname that specifies the location of trusted certificates. Each file in the directory should contain only a single PEM-encoded certificate and should be named with the hash value of the certificate it contains, suffixed with ".0". The hash value of a certificate can be obtained by issuing the following command on the file containing the certificate:

openssl x509 -noout -hash -in cert.pem
spc_x509store_setcrlfile( )

Accepts a filename that specifies a single file containing any number of PEM- encoded CRLs.

For any of the functions, NULL may be specified for the filename or pathname, in which case the system defaults will be used.

void spc_x509store_setcafile(spc_x509store_t *spc_store, char *cafile) {
  if (spc_store->cafile) free(spc_store->cafile);
  spc_store->cafile = (cafile ? strdup(cafile) : 0);
void spc_x509store_setcapath(spc_x509store_t *spc_store, char *capath) {
  if (spc_store->capath) free(spc_store->capath);
  spc_store->capath = (capath ? strdup(capath) : 0);
void spc_x509store_setcrlfile(spc_x509store_t *spc_store, char *crlfile) {
  if (spc_store->crlfile) free(spc_store->crlfile);
  spc_store->crlfile = (crlfile ? strdup(crlfile) : 0);

Additional certificates and CRLs can be added to the store using one of the next two functions. Note that if duplicate certificates or CRLs are included in the spc_x509store_t object, spc_create_x509store( ) will not be able to successfully create an X509_STORE object. These two functions should only be used to add certificates and CRLs to the store that are not present in the certificate file, certificate path, or CRL file.

void spc_x509store_addcert(spc_x509store_t *spc_store, X509 *cert) {
  sk_X509_push(spc_store->certs, cert);
void spc_x509store_addcrl(spc_x509store_t *spc_store, X509_CRL *crl) {
  sk_X509_CRL_push(spc_store->crls, crl);

The last set of functions for manipulating spc_x509store_t objects is used for setting up a certificate verification callback function and for defining flags that control various aspects of the X509_STORE and certificate verification behavior. If no verification callback function is defined, spc_verify_callback( ) is the default; it simply prints any errors encountered out to stderr.

void spc_x509store_setcallback(spc_x509store_t *spc_store,
                              spc_x509verifycallback_t callback) {
  spc_store->callback = callback;
void spc_x509store_setflags(spc_x509store_t *spc_store, int flags) {
  spc_store->flags |= flags;
void spc_x509store_clearflags(spc_x509store_t *spc_store, int flags) {
  spc_store->flags &= ~flags;
int spc_verify_callback(int ok, X509_STORE_CTX *store) {
  if (!ok)
    fprintf(stderr, "Error: %s\n", X509_verify_cert_error_string(store->error));
  return ok;

Only two flags are defined here, leaving plenty of room to expand the implementation and add additional flags as needed:


If this flag is set and no file of trusted certificates has been specified, the system-wide default is used. This flag is checked when creating an X509_STORE object via spc_create_x509store( ).


If this flag is set and no path of trusted certificates has been specified, the system-wide default is not used. This flag is checked when creating an X509_STORE object via spc_create_x509store( ).

The last function, spc_create_x509store( ), creates a new X509_STORE object from the information contained in the spc_x509store_t object that it accepts as its only argument. Attentive readers will notice at this point that we have omitted discussion of several fields in the spc_x509store_t structure. We will address them in Recipe 10.7.

X509_STORE *spc_create_x509store(spc_x509store_t *spc_store) {
  int         i;
  X509_STORE  *store;
  X509_LOOKUP *lookup;
  store = X509_STORE_new(  );
  if (spc_store->callback)
    X509_STORE_set_verify_cb_func(store, spc_store->callback);
    X509_STORE_set_verify_cb_func(store, spc_verify_callback);
  if (!(lookup = X509_STORE_add_lookup(store, X509_LOOKUP_file(  ))))
    goto error_exit;
  if (!spc_store->cafile) {
    if (!(spc_store->flags & SPC_X509STORE_NO_DEFAULT_CAFILE))
      X509_LOOKUP_load_file(lookup, 0, X509_FILETYPE_DEFAULT);
  } else if (!X509_LOOKUP_load_file(lookup, spc_store->cafile, X509_FILETYPE_PEM))
    goto error_exit;
  if (spc_store->crlfile) {
    if (!X509_load_crl_file(lookup, spc_store->crlfile, X509_FILETYPE_PEM))
      goto error_exit;
    X509_STORE_set_flags(store, X509_V_FLAG_CRL_CHECK |
  if (!(lookup = X509_STORE_add_lookup(store, X509_LOOKUP_hash_dir(  ))))
    goto error_exit;
  if (!spc_store->capath) {
    if (!(spc_store->flags & SPC_X509STORE_NO_DEFAULT_CAPATH))
      X509_LOOKUP_add_dir(lookup, 0, X509_FILETYPE_DEFAULT);
  } else if (!X509_LOOKUP_add_dir(lookup, spc_store->capath, X509_FILETYPE_PEM))
    goto error_exit;
  for (i = 0; i < sk_X509_num(spc_store->certs); i++)
    if (!X509_STORE_add_cert(store, sk_X509_value(spc_store->certs, i)))
      goto error_exit;
  for (i = 0; i < sk_X509_CRL_num(spc_store->crls); i++)
    if (!X509_STORE_add_crl(store, sk_X509_CRL_value(spc_store->crls, i)))
      goto error_exit;
  return store;
  if (store) X509_STORE_free(store);
  return 0;

We can now use the functions to manipulate spc_x509store_t objects in verifying an X.509 certificate's validity. The function spc_verify_cert( ) requires an X509 object and spc_x509store_t object. It creates an X509_STORE object from the information in the spc_x509store_t object, and combines it with the X509 object to create an X509_STORE_CTX object as required by X509_verify_cert( ). The return value from spc_verify_cert( ) will be -1 if some kind of error occurred that was not related to the validity of the certificate. If the certificate is valid, the return value will be 1; otherwise, the return value will be 0.

#include <openssl/x509.h>
int spc_verify_cert(X509 *cert, spc_x509store_t *spc_store) {
  int            result = -1;
  X509_STORE     *store = 0;
  X509_STORE_CTX *ctx = 0;
  if (!(store = spc_create_x509store(spc_store))) return -1;
  if ((ctx = X509_STORE_CTX_new(  )) != 0) {
    if (X509_STORE_CTX_init(ctx, store, cert, 0) =  = 1)
      result = (X509_verify_cert(ctx) =  = 1);
  return result;

10.5.4 See Also

Recipe 7.17, Recipe 10.7