Kah – The Developer

STACK_OF Subject Alternate Name and Extended Key Usage Extensions

Posted by: kahgoh on: 29 November, 2008

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 }

Tags: ,

Leave a Reply