Originally published by Robert Beisert at fortcollinsprogram.robert-beisert.com

Tools vs APIs

If you are making a product that will employ unique self-signed certificates and private keys, how would you go about it? The two basic techniques are:

  1. Run a tool like OpenSSL’s cert tool, either at user discretion or through a script
  2. Build the certificate generator directly into your program

Let’s take a quick look at what each solution requires

Tool

The first thing to notice about employing a tool is the size requirement. In order to use whatever product you design, you will have to ship the suite of tools required to produce certificates, which may be bigger than the size of your product (especially if it requires libraries to operate). Obviously, this would be a problem if designing for a microprocessor or similar device, because size is a huge concern.

Second, tools must be accessed. You could use a GUI tool, with all the requirements that entails, or you could go with a console-based tool. Either way, you will need a way to access it. Languages like C implement a “system” function that can call the shell environment, so long as the required parameters are provided.

However, using the tool saves you time. Instead of designing a unique solution to the problem, you can simply require that each user run a script or execute a particular command to run the tool properly. If it doesn’t work, that’s their problem to sort out.

Generally speaking, developers will start with the tools and later implement the code in their product that circumvents it. This allows them to focus on the core of their designs before working on a solution that is already (sub-optimally) implemented.

API

The optimal solution (from a usability standpoint) is to hard-code the tool’s required functionality into the program using provided libraries. This allows the program to “hide” the work that would otherwise be demanded of the user, while simultaneously reducing the footprint of the total program package.

Of course, it’s harder on the programmer. As an example, look at what is required to create a basic self-signed certificate in the GNUTLS library:


**
Generate an X509 certificate for this device
@param output    the certificate that is generated
@param serial    a unique serial number for this certificate
@param key    the private key used to sign this cert
@param uid    the unique id of the issuer and subject (self-signed)
@param dn    A DN used for this certificate (we will generate)
@todo generate functioning certificate using this interface
**/
int generate_certificate    (
gnutls_x509_crt_t *output,
int32_t serial,
gnutls_x509_privkey_t key,
uint64_t uid,
const char * dn
)
{

char * message;
int32_t serial = 0x01111111
//Initialize
gnutls_x509_crt_t local;

error = gnutls_x509_crt_init(&local);

//Key usage details
gnutls_x509_crt_set_key_usage(local, 128|32|16);
//Will need to generate our own (real) dn's
gnutls_x509_crt_set_dn(local, "A Name String", &message);
//Version 3 allows extensions
gnutls_x509_crt_set_version(local, 3);
//TODO Generate key before this runs
gnutls_x509_crt_set_key(local, key);
//Set CA status - will not use
gnutls_x509_crt_set_basic_constraints(local, 0, -1);
//Activation time - should be now
gnutls_x509_crt_set_activation_time(local, time(now));
//Expiritaion time - should be -1 (no expiry)
gnutls_x509_crt_set_expiration_time(local,
GNUTLS_X509_NO_WELL_DEFINED_EXPIRATION);
//Set an alternative name for issuer - see gnutls_x509_subject_alt_name_t
//gnutls_x509_crt_set_issuer_alt_name(local, ENUM_VALUE, void * data
//            int data size, flags);
//Set issuer's unique ID - in this case, a 64-bit number
gnutls_x509_crt_set_issuer_unique_id(
local,
id,
id_size);
//Set policies - probably unnecessary extension
//gnutls_x509_crt_set_policy(local, <policy_t> * policy, uint critical);
//Set serial number of this certificate (TODO establish policy)
gnutls_x509_crt_set_serial(local, &serial, sizeof(int32_t));

//Set subject key id and unique id fields
//ids are void *
//key id should correlate to actual private key
//uid should correlate to this device
gnutls_x509_crt_set_subject_key_id(local, key_id, sizeof key_id);
gnutls_x509_crt_set_subject_unique_id(local, uid, sizeof uid);
//Self-sign the certificate
gnutls_x509_crt_sign(local, local, key);

*output = local;

return 0;
}

That’s thirteen functions which you need to understand and implement properly before you meet the basic requirements for a certificate, plus a number of constants that you have to understand and name correctly. There are a dozen more functions you could choose to employ in your certificates as well, each with their own requirements. When the documentation is difficult to understand and provides little in the way of examples, it can take a while to design a hard-coded solution.

However, on the positive side, once you’ve solved the problem once, you don’t have to solve it again. You can take this code (assuming it works as you want) and build it directly into your product.

photo by: