next up previous contents
Next: Example 1: Signing Up: OpenSSL and EVP Previous: Code and documentation   Contents

Some general functions

First of all, let's do the unavoidable initalization. As mentioned before, to make full use of OpenSSL's possibilities, we need to load a configuration that the user may have set.

#include <openssl/conf.h>
#include <openssl/engine.h>
#include <openssl/err.h>

void
initialize()
{
     OPENSSL_config(NULL);
     OpenSSL_add_all_digests();
     ERR_load_crypto_strings();
}

Since its argument is NULL, OpenSSL_config loads the configuration file set in the environment variable OPENSSL_CONF.

We also call OpenSSL_add_all_digests. This loads digest algorithm names, so we can later initialize them by name (see example 2, verification, and the call to EVP_get_cipher_by_name).

The third call here loads human-readable error messages into memory, this is useful information when something goes wrong.

Like most API's, a lot of OpenSSL methods return a status code that signifies whether the function call succeeded. OpenSSL also provides a feedback mechanism similar to errno, so that a user can see some more information than just an error code. Let's make a small helper function that can assist us with that.

void
check_ssl_rv(const char *function_name,
             const int return_value,
             const int success_value)
{
     if (return_value != success_value) {
          fprintf(stderr,
                  "Error, return value of %s was %d",
                  function_name,
                  return_value);
          fprintf(stderr, " should have been %d\n", success_value);
          fprintf(stderr, "OpenSSL error messages:\n");
          ERR_print_errors_fp(stderr);
          exit(ERR_get_error());
     }
}

For completeness, we do not only pass a return value we received to this function, but also the value that would signify success, just in case this is non-standard. If there is an error, we use ERR_print_errors_fp, which, if we have loaded the error strings earlier, will print a nice list of errors.

Finally, if there was an error, we bluntly exit to the system. Naturally, you might want to implement a more graceful error handling routine.

If we perform global initialization, we probably need some cleanup too.

void
clean_up()
{
     ERR_remove_state(0);
     ERR_free_strings();

     ENGINE_cleanup();
     EVP_cleanup();

     CONF_modules_finish();
     CONF_modules_free();
     CONF_modules_unload(1);

     CRYPTO_cleanup_all_ex_data();
}

ERR_remove_state frees up the error message queue for the current thread. With a non-zero argument, you can specify the thread that should have its error queue cleaned up. ERR_free_strings removes the strings we loaded in the initialization function.

ENGINE_cleanup cleans any memory that is allocated dynamically by engine functions. This happens for instance when certain engines are loaded, even if they are not actually used. See the $engine(3)$ manual page for more information.

EVP_cleanup removes memory allocated when loading digest and cipher names in the OpenSSL_add_all_ family of functions, like OpenSSL_add_all_digests we called earlier.

Next we free and unload any configuration modules that may have been loaded. The argument to CONF_modules_unload is 1, telling OpenSSL to unload both dynamically loaded modules and builtin modules. With this argument set to 0, it would not unload builtin modules.

CRYPTO_cleanup_all_ex_data cleans some more generally used memory. Documentation for this function can be hard to find. Just know that it does free some memory, and that this call is not thread-safe.

Now that we have the global tidying up sorted, let's go to some specific engine-related functions.

ENGINE *
select_engine(const char *engine_name)
{
     int rv;
     ENGINE *next_engine;
     ENGINE *engine = ENGINE_by_id(engine_name);

     if (!engine) {
          fprintf(stderr, "Unable to load engine: %s\n", engine_name);
          engine = ENGINE_get_first();
          fprintf(stderr, "Available engines:\n");
          while(engine) {
               fprintf(stderr, "%s\n", ENGINE_get_id(engine));
               next_engine = ENGINE_get_next(engine);
               ENGINE_free(engine);
               engine = next_engine;
          }
          exit(EXIT_FAILURE);
     }

     rv = ENGINE_init(engine);
     check_ssl_rv("ENGINE_init", rv, 1);

     return engine;
}

First we get a structural reference to the engine we need by calling ENGINE_by_id with the correct name, in our case 'pkcs11'. If this call fails, apparently the right modules are not loaded or the engine name is unknown. In that case we'll try to be bit userfriendly and print a list of available engines.

Internally, OpenSSL maintains a dynamic list of available engines. You can simply get the first of that list by calling ENGINE_get_first, and walk through that list by calling ENGINE_get_next until that return NULL. These functions create structural references that should be freed.

If it succeeds, we have a structural reference to our engine. However, if we want to actually do anything with it, we need to initialize it. To use the terms of the OpenSSL documentation, we need to create a functional reference to it. This is what ENGINE_init does. This function returns 1 on success, so we check for that.

We might want to be ready and add a little engine-specific cleanup too:

void
clean_engine(ENGINE *engine)
{
     ENGINE_finish(engine);
     ENGINE_free(engine);
}

ENGINE_finish removes the functional reference, and ENGINE_free removes the structural reference.

So, with our engine up and running, it's time to see whether it has any key data. Since EVP does not provide key-management functionality, we can skip over key enumeration and key creation. You'll need to use specific tools for that, or go straight to PKCS.

We'll just assume there is at least one key present, and load it:

EVP_PKEY *
get_private_key(ENGINE *engine, const char *id)
{
     EVP_PKEY *key = NULL;

     key = ENGINE_load_private_key(engine, id, UI_OpenSSL(), NULL);

     if (!key) {
          fprintf(stderr, "Error loading private key with id %s\n", id);
     }
     return key;
}

EVP stores its keys in EVP_PKEY structures. Private keys, public keys, and secret keys are all stored in this structure. We load a private key with ENGINE_load_private_key.

The key id is not just a simple byte string. OpenSSL gives you quite a few options here. It is a null-terminated string which can be one of the following forms:

Where:

The third argument provides a way for the application developer to specify how a user can provide the user PIN to the HSM. A nice default is UI_OpenSSL, which just uses the command line to enter a PIN when needed. The fourth argument is an optional callback argument for the UI function. UI_OpenSSL does not need an argument, so here it's NULL.

Well if we can get private keys that easily, let's just do the public counterpart as well:

EVP_PKEY *
get_public_key(ENGINE *engine, const char *id)
{
     EVP_PKEY *key = NULL;
     key = ENGINE_load_public_key(engine, id, UI_OpenSSL(), NULL);

     if (!key) {
          fprintf(stderr, "Error loading public key with id %s\n", id);
     }
     return key;
}


next up previous contents
Next: Example 1: Signing Up: OpenSSL and EVP Previous: Code and documentation   Contents
Written by Jelte Jansen
© NLnet Labs, May 13, 2008
jelte@nlnetlabs.nl