[OpenSSL] Verifying Using a Certificate Store

An certificate store can be used to hold multiple CA certificates. It is used
for verification purposes, when you have multiple CA certificates, but you
do not necessarily know which of those CA certificates signed the
certificate. When verifying the certificate, OpenSSL will look for the CA
certificate or the chain in the store.

Setting up the store programatically involves allocating memory for the
store via X509_STORE_new and then loading certificates into it. There
are two ways of placing the certificates into the store – by loading and
adding each individual certificate or supplying the location of CA
certificates. For this tutorial, we’ll load each certificate programatically.

 1 void verifyCertificate()
 2 {
 3   X509 *cert = loadCert("cert.pem");
 4   X509_STORE *store = X509_STORE_new();
 5
 6   loadToStore("cert-1.pem", store);
 7   loadToStore("cert-2.pem", store);
 8   loadToStore("cert-3.pem", store);
 9

The listing for loading the certificate into the store will be shown later.
Verification is performed using a store context. Setting up a context
involves allocating space for a store context and initialising it.

10   // Create the context to verify the certificate.
11   X509_STORE_CTX *ctx = X509_STORE_CTX_new();
12
13   // Initial the store to verify the certificate.
14   X509_STORE_CTX_init(ctx, store, cert, NULL);
15
16   X509_verify_cert(ctx);
17
18   X509_STORE_CTX_cleanup(ctx);
19   X509_STORE_CTX_free(ctx);
20   X509_STORE_free(store);
21   ctx = NULL;
22   store = NULL;
23 }
24

The third parameter to X509_STORE_CTX_init, cert, is the
certificate that is going to be verified. The call to X509_verify_cert
should also return the value one, if verification succeeds.

Adding a certificate to the store requires the certificate to be read first.

25 void loadToStore(std::string file, X509_STORE *&store)
26 {
27   X509 *cert = loadCert(file);
28   if (cert != NULL)
29   {
30     X509_STORE_add_cert(store, cert);
31   }
32   else
33   {
34     std::cout << "Can not load certificate " 
35       << file << std::endl;
36   }
37 }
38
39 X509 *loadCert(std::string file)
40 {
41   FILE *fp = fopen(file.c_str(), "r");
42   X509 *cert = PEM_read_X509(fp, NULL, NULL, NULL);
43   fclose(fp);
44   return cert;
45 }

The process of adding the certificate should be self-explanatory from the listing.

[OpenSSL] STACK_OF Subject Alternate Name and Extended Key Usage Extensions

In A Certificate’s Subject, Issuer and its keyUsage, a certificate was read into memory and its subject and issuer names and its key usage extension contents were outputted. Here, the subject alternate names and extended key usage names are extracted. Both of these extensions are defined in RFC 3280 as a SEQUENCE of something. In the code, this would mean the extension would be parsed into a STACK_OF something.

The ASN.1 description for the extended key usage as defined in RFC 3280:

ExtKeyUsageSyntax ::= SEQUENCE SIZE (1..MAX) OF KeyPurposeId

KeyPurposeId ::= OBJECT IDENTIFIER

As mentioned before, the extended key usage is an ASN.1 SEQUENCE, which in our code will be a STACK_OF something. The definition states its a SEQUENCE of KeyPurposeId, which is just an OBJECT IDENTIFIER. In our code, we would extract this as an ASN1_OBJECT. Since KeyPurposeId is an OBJECT IDENTIFIER (aka OID), there will be a unique object for each extended key usage value type. The name of the extend key usage value in the extension can be obtained from OpenSSL.


 1 #include <iostream>
 2 #include <openssl/pem.h>
 3 #include <openssl/x509v3.h>
 4
 5 // Extract the extended key usage values from the
 6 // certificate and output them.
 7 void extractExtendedKeyUsage (X509 *cert)
 8 {
 9   std::cout << std::endl <<
10     "Extended key usages: ";
11   int usageId = 0;
12   const char *kuValue = NULL;
13   STACK_OF(ASN1_OBJECT) *extKu =
14     (STACK_OF(ASN1_OBJECT) *)
15     X509_get_ext_d2i(cert, NID_ext_key_usage,
16      NULL, NULL);
17

As with the stack data structure, a STACK_OF(ASN1_OBJECT) support pop and push operations. With each object in the stack, you can obtain its numeric identifier (NID) or a textual representation of its name by using the OBJ functions. For the code above, it will give back each extend key usage value in the certificate.

18   while (sk_ASN1_OBJECT_num(extKu) > 0)
19   {
20     usageId = OBJ_obj2nid(
21       sk_ASN1_OBJECT_pop(extKu));
22     kuValue = OBJ_nid2sn(usageId);
23     std::cout << " [" << kuValue << "]";
24   }
25   std::cout << std::endl;
26 }
27

The subject alternate name’s ASN.1 definition is:

SubjectAltName ::= GeneralNames

GeneralNames ::= SEQUENCE SIZE (1..MAX) OF GeneralName

GeneralName ::= CHOICE {
otherName [0] OtherName,
rfc822Name [1] IA5String,
dNSName [2] IA5String,
x400Address [3] ORAddress,
directoryName [4] Name,
ediPartyName [5] EDIPartyName,
uniformResourceIdentifier [6] IA5String,
iPAddress [7] OCTET STRING,
registeredID [8] OBJECT IDENTIFIER }

OtherName ::= SEQUENCE {
type-id OBJECT IDENTIFIER,
value [0] EXPLICIT ANY DEFINED BY type-id }

EDIPartyName ::= SEQUENCE {
nameAssigner [0] DirectoryString OPTIONAL,
partyName [1] DirectoryString }

Notice that ASN.1 definition states that a subject alternate name is a sequence of GeneralName. It also happens that OpenSSL has define a structure called GENERAL_NAME in x509v3.h.

 1 typedef struct GENERAL_NAME_st {
 2
 3 #define GEN_OTHERNAME 0
 4 #define GEN_EMAIL 1
 5 #define GEN_DNS   2
 6 #define GEN_X400  3
 7 #define GEN_DIRNAME 4
 8 #define GEN_EDIPARTY  5
 9 #define GEN_URI   6
10 #define GEN_IPADD 7
11 #define GEN_RID   8
12
13 int type;
14 union {
15   char *ptr;
16   OTHERNAME *otherName; /* otherName */
17   ASN1_IA5STRING *rfc822Name;
18   ASN1_IA5STRING *dNSName;
19   ASN1_TYPE *x400Address;
20   X509_NAME *directoryName;
21   EDIPARTYNAME *ediPartyName;
22   ASN1_IA5STRING *uniformResourceIdentifier;
23   ASN1_OCTET_STRING *iPAddress;
24   ASN1_OBJECT *registeredID;
25
26   /* Old names */
27   ASN1_OCTET_STRING *ip; /* iPAddress */
28   X509_NAME *dirn;    /* dirn */
29   ASN1_IA5STRING *ia5;/* rfc822Name, dNSName, uniformResourceIdentifier */
30   ASN1_OBJECT *rid; /* registeredID */
31   ASN1_TYPE *other; /* x400Address */
32 } d;
33 } GENERAL_NAME;

Notice that the definition of the GENERAL_NAME structure in the header mirrors that of as defined in the RFC. The member type reveals exactly kind of information is in the structure and, hence, which member of the union, d, to use to access the actual data. In case you are wondering, the definition for the OTHERNAME and EDIPARTYNAME structures are also defined in the same file. Again, their definitions mirrors that as specified by the RFC.

1 typedef struct otherName_st {
2 ASN1_OBJECT *type_id;
3 ASN1_TYPE *value;
4 } OTHERNAME;
5
6 typedef struct EDIPartyName_st {
7   ASN1_STRING *nameAssigner;
8   ASN1_STRING *partyName;
9 } EDIPARTYNAME;

First, to obtain the subject alternate name:

28 // Extract the subject alternate name and output
29 // its contents.
30 void extractSubjectAltName (X509 *cert)
31 {
32   std::cout << std::endl <<
33     "Subject alternate name" << std::endl;
34   GENERAL_NAME *namePart = NULL;
35   STACK_OF(GENERAL_NAME) * san =
36     (STACK_OF(GENERAL_NAME) *)
37     X509_get_ext_d2i(cert,
38       NID_subject_alt_name, NULL, NULL);
39
40   while (sk_GENERAL_NAME_num(san) > 0)
41   {
42     std::cout << "Entry type: ";
43     namePart = sk_GENERAL_NAME_pop(san);
44

Next, we can output the information. The type of information in the GENERAL_NAME is given by the member type, which will also indicate which of the other in the union to use to access the data. Since there are many different types of entry values possible in a GENERAL_NAME, I’ve kept it short here by outputting only URI and email entries. Note that the email entry is stored in the rfc822Name member (yes, RFC 822 is about email, although it has been updated by later RFCs).

45     // Determine what kind of data we have
46     // and handle it appropriately.
47     switch (namePart->type)
48     {
49       case GEN_URI:
50         std::cout << "URI = " <<
51           ASN1_STRING_data(namePart->
52           d.uniformResourceIdentifier);
53         break;
54       case GEN_EMAIL:
55         std::cout << "Email = " <<
56         ASN1_STRING_data(namePart->
57         d.rfc822Name);
58         break;
59       default:
60         // Going to be lazy for this
61         // example.
62         std::cout << "Unrecognisable";
63         break;
64     }
65     std::cout << std::endl;
66   }
67 }

[OpenSSL] A Certificate’s Subject, Issuer and its keyUsage

The first code that I ever wrote using OpenSSL was one that read the certificate into memory and examined the values of the keyUsage extension. In fact, OpenSSL provides the functionality to extract all of the information from an X509 certificate that you possibly want. In this tutorial, an X509 certificate is read into memory and the subject and issuer are extracted and the keyUsage extension is examined.

The OpenSSL library provides a few different ways of loading a certificate into memory. For certificates that are in the DER format, the d2i_X509 functions can be used. OpenSSL provides two of these functions – one to read from a file pointer and another to read from a BIO. For anything in the PEM format, openssl provides a set of PEM_read functions. Again, there is a function to read from a BIO and another to read from a file pointer.

  1 #include <iostream>
  2
  3 #include <openssl/pem.h>
  4
  5 // Prototypes
  6 void printSubject (X509 *cert);
  7 void breakdownIssuer (X509 *cert);
  8 void extractNameEntry (X509_NAME *name, int nid);
  9 void checkKeyUsageExtension (X509 *cert);
 10 void testKeyUsage (ASN1_BIT_STRING *keyUsage,
 11   int usageBit, const char *usageName);
 12
 13 int main (int argc, char *argv[])
 14 {
 15   FILE *certFile = fopen("cert.pem", "r");
 16   X509 *cert = NULL;
 17
 18   if (certFile != NULL)
 19   {
 20     cert = PEM_read_X509(certFile, NULL, NULL,
 21       NULL);
 22     printSubject(cert);
 23     breakdownIssuer(cert);
 24     checkKeyUsageExtension(cert);
 25     fclose(certFile);
 26   }
 27   else
 28   {
 29     std::cout << "Could not open file cert.pem";
 30   }
 31
 32   X509_free(cert);
 33 }
 34

Extracting the certificate’s subject name is as simple as a single function call.

 35 // Simple function that prints out the subject name
 36 // using the RFC2253 format.
 37 void printSubject (X509 *cert)
 38 {
 39   std::cout << "— Certificate subject" <<
 40     std::endl;
 41   X509_NAME *subject = X509_get_subject_name(cert);
 42

With the subject name stored in a X509_NAME structure, we can go throught each of the structure’s members and output their values. Alternatively, if we want to obtain a readable format of the entire subject name, we can use OpenSSL’s X509_NAME_print_ex function. However, this function outputs the subject name to a BIO. Fortunately, it is quite easy to extract the string from the BIO and make our own copy.

 43   // Set up a BIO to put the subject line into.
 44   BIO *subjectBio = BIO_new(BIO_s_mem());
 45
 46   // Now, put the subject line into the BIO.
 47   X509_NAME_print_ex(subjectBio, subject, 0,
 48     XN_FLAG_RFC2253);
 49
 50   // Obtain a reference to the data and copy out
 51   // just the length of the data.
 52   char *dataStart = NULL;
 53   char *subjectString = NULL;
 54   long nameLength = BIO_get_mem_data(subjectBio,
 55     &dataStart);
 56   subjectString = new char[nameLength + 1];
 57   memset(subjectString, 0x00, nameLength + 1);
 58   memcpy(subjectString, dataStart, nameLength);
 59
 60   std::cout << "Certificate subject name: " <<
 61     subjectString << std::endl;
 62 }
 63

If you had a look at the documentation for X509_NAME_print_ex, you might have noticed the function X509_NAME_oneline and X509_NAME_print would have done also printed out the subject name in a readable format, with far less code. So then, why have I not used X509_NAME_oneline? It is because the same documentation also states that the two functions are “legacy functions which produce a non standard output form, they don’t handle multi character fields and have various quirks and inconsistencies. Their use is strongly discouraged in new applications.”.

Next, we’ll extract the issuer name and extract the individual parts, such as the common name, country and organisation names. Since the issuer and subjects are stored and retrieved using the X509_NAME structure, this same method can be applied to the subject name (and a readable form of the issuer name can be obtained using the same method as above).

 64 // Simple method that takes the issuer name to
 65 // pieces and prints out a couple of the fields.
 66 void breakdownIssuer (X509 *cert)
 67 {
 68   std::cout << std::endl << "— Issuer breakdown" 
 69     << std::endl;
 70   X509_NAME *issuer = X509_get_issuer_name(cert);
 71
 72   extractNameEntry(issuer, NID_commonName);
 73   extractNameEntry(issuer, NID_countryName);
 74   extractNameEntry(issuer, NID_organizationName);
 75 }
 76

The function extractNameEntry is defined later and is where the individual fields in the issuer name will be extracted. The constants NID_commonName, NID_countryName and NID_organizationName are defined by OpenSSL and references the fields in the issuer name to be extracted. There are defined in objects.h and obj_mac.h.

To extract the actual field from the issuer name, we must determine the location of the field in the name and then extract the entry as a X509_NAME_ENTRY.

 77 // Obtains an entry from a X509 name (i.e. either
 78 // the certificate’s issuer or subject) and
 79 // prints out on standard output.
 80 void extractNameEntry (X509_NAME *name, int nid)
 81 {
 82   // First, let us get the common name from the
 83   // certificate.
 84   int position = X509_NAME_get_index_by_NID(name,
 85     nid, –1);
 86   X509_NAME_ENTRY *entry = X509_NAME_get_entry(
 87     name, position);
 88     

Obtaining a readable string representation of the field requires one more level extraction and conversion.

 89   // This will obtain just the name entry.
 90   ASN1_STRING *asn1Data =
 91     X509_NAME_ENTRY_get_data(entry);
 92
 93   // Next use obtain the actual string data and
 94   // output to standard output.
 95   unsigned char *entryString = ASN1_STRING_data(
 96     asn1Data);
 97
 98   std::cout << OBJ_nid2ln(nid) << ": " <<
 99     entryString << std::endl;
100 }
101

The function OBJ_nid2ln converts the our NID value that we passed in to the name of the field.

Lastly, we’ll extract the keyUsage extension and examine its contents. The method of extracting the extension is same for any extension. RFC 3820 provides a description of the keyUsage extension (actually, it also provides descriptions of other extension as well). Have a look at the RFC if you also want to know what the extension is for. The keyUsage extension is encoded as a bit string, so in OpenSSL, this is extracted as bit string. Furthermore, take note of the meaning of each of the bits in the bit string from RFC.

102 // Extracts the key usage extension from the
103 // certificate.
104 void checkKeyUsageExtension (X509 *cert)
105 {
106   int criticality = –1;
107   int extIndex = –1;
108
109   std::cout << std::endl <<
110     "— Key Usage extension" << std::endl;
111
112   // Obtain the keyUsage extension in the form
113   // of a bit string.
114   ASN1_BIT_STRING *keyUsage = (ASN1_BIT_STRING *)
115     X509_get_ext_d2i(cert, NID_key_usage,
116     &criticality, &extIndex);
117
118   // Output the contents of the keyUsage
119   // extension.
120   const char *usages[] = {"digitalSignature",
121                           "nonRepudiation",
122                           "keyEncipherment",
123                           "dataEncipherment",
124                           "keyAgreement",
125                           "keyCertSign",
126                           "cRLSign",
127                           "encipherOnly",
128                           "decipherOnly"};
129
130   for (int index = 0; index < 8; index++)
131   {
132     testKeyUsage(keyUsage, index, usages[index]);
133   }
134 }
135
136 // Tests the value of the key usage extension to
137 // determine for a given value and outputs the
138 // result to standard output.
139 //
140 // The parameter usageBit is which bit in the
141 // keyUsage to check. RFC 3280 lists the meaning
142 // of each of the bits in the extension.
143 void testKeyUsage (ASN1_BIT_STRING *keyUsage,
144   int usageBit, const char *usageName)
145 {  
146   std::cout << usageName << ": " <<
147     ASN1_BIT_STRING_get_bit(keyUsage, usageBit) <<
148     std::endl;
149 }
150

If you compile and run this program, you should see the output of the contents of cert.pem‘s subject and issuer names and its keyUsage extension (if it can find the certificate). More importantly, note one thing that has been omitted in the sample code above – the lack of checking for successful operations. Throughout the code, I had not checked for things such as whether the subject name was successfully obtained from the certificate, whether we managed to get the individual fields from the issuer name or a keyUsage extension really exists before their contents are used. This was omitted to shorten the amount of code that would have to be displayed in this tutorial. Adding the checks yourself is as simple as checking the return values (usually, a null or -1 is returned on error).

[OpenSSL] Decrypting with the OpenSSL API

Before you can decrypt something, you have to generate it! You can generate encrypted data with OpenSSL on the commandline using the enc command. The encryption process requires a key and iv (initialisation vector) pair, which can be derived from a given passphrase. The enc command gives you the option of specifying, either the passphrase or the key and iv pair. For this short tutorial, let’s use specify the key and iv pair on the command line. To use perform the encryption, use the following command:

openssl enc -e -bf-cbc -in test.txt -out test.bin \
     -K 12345678911234567892123456789312 \
     -iv 00000000

The above command instructs OpenSSL to perform encryption (“-e”) on the file test.txt (“-in test.txt”), using the blowfish with cipher block chaining (“-bf-cbc”). The encrypted data would be placed in the file “test.bin” (“-out test.bin”). The key, in hexadecimal, is 12345678911234567892123456789312 (“-K …”) and the iv is 00000000 (-“iv …”). Notice that the -K is in capital letters. The lower case k has a different meaning! The key and iv must also expect their parameters to be in hexadecimal. To decrypt the encrypted file on the command line, you also use the enc command. The only differences are that the use ‘-d’ instead of ‘-e’ and you do not need the “-out …” argument.

 openssl enc -e -bf-cbc -in test.bin \
    -K 12345678911234567892123456789312 \
    -iv 00000000 

The functions for performing encryption and decryption in the OpenSSL crypto library are EVP_EncryptInit_ex, EVP_EncryptUpdate and EVP_EncryptFinal_ex, for encryption, and EVP_DecryptInit_ex, EVP_DecryptUpdate and EVP_DecryptFinal_ex, for decryption. The documentation for these functions include an example for how to perform encryption using the encryption functions. The code for decryption is similar to the code for encryption.

First, start by loading the key, iv and the encrypted data.  The encrypted data can be loaded using the standard combination of fopen, fread and fclose.


int readfile(const char *filePath,
             unsigned char * buf,
             int maxSize)
{
    FILE * inf = NULL;
    int readSize = 0;
    inf = fopen(filePath, "rb") ;

    if (inf != NULL)
    {
        readSize = fread(buf, 1,
            maxSize, inf) ;
        fclose(inf);
    }

    return readSize;
}

If you are using encrypted data created using the same key and iv combination as above, the key and iv should be specified as follows:

    char key[16] = {0x12, 0x34, 0x56, 0x78, 0x91, 0x12, 0x34, 0x56,
                     0x78, 0x92, 0x12, 0x34, 0x56, 0x78, 0x93, 0x12};
 
    char iv[8];
    memset(iv, 0x00, 8);

Notice that the key and iv are specified in hexadecimal. This is because when the encrypted data as generated, the key and iv were also given in hex! This is also important, as specifying it has a literal string (ie. char *iv = “00000000”) is not the same and could result in time spent in trying to debug it fustratingly!

As mentioned before, the code for decrypting is similar to the code for encrypting. The first step is to setup a cipher context and initialise it.

char *decrypt (char *key,
               char *iv,
               char *encryptedData,
               int encryptedLength)
{
    // Initialisation
    EVP_CIPHER_CTX *cryptCtx = EVP_CIPHER_CTX_new();
    EVP_CIPHER_CTX_init(cryptCtx);

    int decryptedLength = 0;
    int allocateSize = encryptedLength * sizeof(char);
    int lastDecryptLength = 0;

    char *decryptedData = (char *) malloc (allocateSize);
    memset(decryptedData, 0x00, allocateSize);

    int decryptResult = EVP_DecryptInit_ex(cryptCtx,
        EVP_bf_cbc(), NULL, key, iv);

Next, use the EVP_DecryptUpdate function to attach the encrypted data and to decrypt the data.

    // EVP_DecryptInit_ex returns 1 if it succeeded.
    if (decryptResult == 1)
    {
        decryptResult = EVP_DecryptUpdate(cryptCtx, decryptedData,
            &decryptedLength, encryptedData, encryptedLength);

Note that EVP_DecryptUpdate will alter the value of the third parameter to be equal to the amount of data that was written. This is not always the entire length of the decrypted data! To finish the decryption process, use EVP_DecryptFinal_ex. This will decrypt any remaining data.

        // Cleanup
        if (decryptResult == 1)
        {
            // Stick the final data at the end of the last
            // decrypted data.
            EVP_DecryptFinal_ex(cryptCtx,
                decryptedData + decryptedLength,
                &lastDecryptLength);

            decryptedLength = decryptedLength + lastDecryptLength;
            decryptedData[decryptedLength – 1] = ;
            printf ("Decrypted size: %d\n", decryptedLength);
            printf ("Decrypted data: \n%s\n\n", decryptedData);
        }
        else
        {
            printf ("EVP_DeccryptUpdate failure.\n");
        }
    }
    else
    {
        printf ("EVP_DecryptInit_ex failure.\n");
    }

    EVP_CIPHER_CTX_free(cryptCtx);
    EVP_cleanup();
    return decryptedData;
}

In the call to EVP_DecryptFinal_ex, note that the pointer that is given is the start of the decrypted memory buffer plus the length that was given back by the call to EVP_DecryptUpdate. This to prevent it from writing over what was originally decrypted. The resulting decrypted data is NOT guaranteed to be always null terminated. This is why a null has also been explicitly set in the above code.

[OpenSSL] Decoding Base 64 with OpenSSL

Base 64 is a form of encoding that converts groups of 24 bits to four ASCII characters. The details of base 64 encoding is detailed on the Base 64 (Wikipedia) page. The information on that page should be enough for you to write your own base 64 decoder. Alternatively, we can use OpenSSL to do it for us.

The OpenSSL documentation on BIO_f_base64 actually provides an example of how to use the library to perform the decoding with the library. The example on that page:

 
    BIO *bio, *b64, *bio_out;  
    char inbuf[512];  
    int inlen;  

    b64 = BIO_new(BIO_f_base64());  
    bio = BIO_new_fp(stdin, BIO_NOCLOSE);  
    bio_out = BIO_new_fp(stdout, BIO_NOCLOSE);  
    bio = BIO_push(b64, bio);  

    while((inlen = BIO_read(bio, inbuf, 512)) > 0)         
        BIO_write(bio_out, inbuf, inlen);

In the example, three BIOs are created – for decoding, outputting to standard output and another for taking in encoded data. The call to BIO_push creates a BIO chain (it adds the input BIO to the base 64 BIO) and then we loop until all of the decoded data is read. In each iteration, the decoded data is, first, read into the character array inbuf before it is outputted to standard output by writing to the BIO bio_out. In the example, each chunk of decoded data is outputted to standard output, but it is likely that you would want to store the decoded data in memory for some sort of processing. The most obvious way of doing this is to allocate memory to hold the entire decoded data and append each decoded chunk of data during each iteration of the loop.

If you are using C++, you can easily use C++ string to hold the decoded data and append during each loop. If you are using just C, one possibility around this problem is to allocate some initial memory and then use realloc to resize the memory during each iteration of the loop to hold in more and more data. But this seems rather inefficient because of the need to increase the size of the allocated memory. Note that realloc could also have to move the memory. We could avoid having to resize the memory during each iteration by allocating the memory before reading back the decoded data. Another inefficient method is to perform the loop twice – once to determine the length of the decoded data and another time to store the decoded data. It is more efficient to simply allocate the memory first and then read through the loop once, but how do you know the length of decoded data?

To determine exactly how long the decoded data should be, you have to go back to how base 64 encoding works – a group of 24 bits is mapped to a group of four ASCII characters. Note that valid base 64 encoded data will always have a length that is divisible by four. If the length of base 64 encoded data is not divisible for four, there is something wrong with the encoded data. For are given encoded data with x number of ASCII characters, the number of bits, y, in the decoded data is found as follows:

        24 bits = 4 ASCII characters 
        y bits = x ASCII characters 
        y = (24 * x) / 4 = (6 * x) bits

Next, we have to account for padding. Padding is present when the last group of 24 bits in the data that was encoded did fully occupy 24 bits. The base 64 standard states that there are three possibilities for padding – there will be either no, only one or only two padding characters (see the bottom of page 5 in RFC 4648). Also, note that the standard uses the equal sign (“=”) as the padding character. For each instance of this padding character at the end of the encoded data, subtract eight bits from y to get the final expected decoded length.

Now that the calculation has been explained, we can turn this into code:

     char *encoded = "WU9ZTyEA";
     int result = 0;
     int padding = 0;
     int strLength = strlen(encoded);

     // Check that the string is not empty and that the length is a multiple of four.
     if ((strLength > 0) && ((strLength % 4) == 0))     
     {
         // First, we check if the last character is padding.
         if (encoded[strLength - 1] == PADDING_CHAR)
         {
             padding++;

             // The second last character could also be padding!
             if (encoded[strLength - 2] == PADDING_CHAR)
             {
                 padding++;
             }
         }

         // Now that we know the amount of padding, we can caculate the expected
         // length. If groups of 24 bits (3 characters) get encoded into 32 bits
         // (4 characters) ...
         result = (3 * strLength) / 4;

         // Accounting for the padding:
         result = result - padding;
     }
     else
     {
         printf("Either there is no data to decode or its length is incorrect.\n");
     }

Note one important assumption was made in this code … that one char occupies 8 bits!

There is also one thing that I have come across when using OpenSSL to perform base 64 decoding. If the encoded data is greater than 64 characters long, I have had to insert a newline character after every 64 characters. This could be because OpenSSL mainly deals with the PEM format, which uses base 64 encoding. The PEM format uses line lengths of 64 characters (see RFC 4648, section 3.1). I have also had to make sure that the last line of the encoded data has a newline character at the end.

[OpenSSL] An Intro to OpenSSL

I first came across OpenSSL when I first started work at Motorola in Perth, Western Australia. In their own words, the OpenSSL project:

Open Source toolkit implementing the Secure Sockets Layer (SSL v2/v3) and Transport Layer Security (TLS v1) protocols as well as a full-strength general purpose cryptography library.

Once installed, we can use OpenSSL to generate a chain of X.509 certificates. To this, a self-signed certificate must first be generated. This will become the root certificate.

  1. First, create a private key.
    openssl genrsa > [root key file]
  2. Generate the self signed certificate.
    openssl req -x509 -key [key file] -new -out [cert file]

Now that we have the first certificate in the chain, we can generate the other certificates in the chain.

  1. Create a key for the certificate.
    openssl genrsa > [root key file]
  2. Generate a certificate signing request (CSR) for the certificate.
    openssl req -new -key [key file] -out [request file]
  3. Sign the CSR with the root certificate.
    openssl x509 -CAkey [root key file] \
    -CA [root certificate file] \ 
    -CAcreateserial -req \ 
    -in [certificate signing request] \ 
    -out [output certificate]

Following the these steps will give you a chain of two certificates. You generate a longer chain by generating keys and certificate signing requests for each of the certificates and then signing them, as just above. The only difference is that, in the final step, you have to substitute the root key file and root certificate file with the certificate’s issuer key and certificate file.

Once the certificates are generated, you can have a look back at the contents of the certificates, by using the following commands:
openssl x509 -text -noout -in [certificate file]

There is a whole bunch of other things that we can do with OpenSSL, but I’ll leave that for another time!