[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.

Advertisements

[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] 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!