moved RSA key size constraints to definitions.h
[strongswan.git] / src / libstrongswan / crypto / x509.c
index 9123b63..54a6395 100755 (executable)
@@ -27,6 +27,8 @@
 
 #include "x509.h"
 
+#include <types.h>
+#include <definitions.h>
 #include <asn1/oid.h>
 #include <asn1/asn1.h>
 #include <asn1/pem.h>
 #include <utils/linked_list.h>
 #include <utils/identification.h>
 
-#define BUF_LEN 512
-#define BITS_PER_BYTE  8
-#define RSA_MIN_OCTETS (1024 / BITS_PER_BYTE)
-#define RSA_MIN_OCTETS_UGH     "RSA modulus too small for security: less than 1024 bits"
-#define RSA_MAX_OCTETS (8192 / BITS_PER_BYTE)
-#define RSA_MAX_OCTETS_UGH     "RSA modulus too large: more than 8192 bits"
+#define CERT_WARNING_INTERVAL  30      /* days */
 
-logger_t *logger;
-
-typedef struct generalName_t generalName_t;
+static logger_t *logger;
 
 /**
- * A generalName, chainable in a list
+ * Different kinds of generalNames
  */
-struct generalName_t {
-       generalName_t *next;
-       generalNames_t kind;
-       chunk_t name;
+typedef enum generalNames_t generalNames_t;
+
+enum generalNames_t {
+       GN_OTHER_NAME =         0,
+       GN_RFC822_NAME =        1,
+       GN_DNS_NAME =           2,
+       GN_X400_ADDRESS =       3,
+       GN_DIRECTORY_NAME =     4,
+       GN_EDI_PARTY_NAME = 5,
+       GN_URI =                        6,
+       GN_IP_ADDRESS =         7,
+       GN_REGISTERED_ID =      8,
 };
 
 typedef struct private_x509_t private_x509_t;
@@ -111,11 +114,6 @@ struct private_x509_t {
        linked_list_t *subjectAltNames;
        
        /**
-        * List of identification_t's representing issuerAltNames
-        */
-       linked_list_t *issuerAltNames;
-       
-       /**
         * List of identification_t's representing crlDistributionPoints
         */
        linked_list_t *crlDistributionPoints;
@@ -140,12 +138,16 @@ struct private_x509_t {
         */
        chunk_t authKeySerialNumber;
        
+       /**
+        * CA basic constraints flag
+        */
+       bool isCA;
+
        u_char authority_flags;
        chunk_t tbsCertificate;
        /*   signature */
        int sigAlg;
        chunk_t subjectPublicKey;
-       bool isCA;
        bool isOcspSigner; /* ocsp */
        chunk_t accessLocation; /* ocsp */
        /* signatureAlgorithm */
@@ -154,6 +156,50 @@ struct private_x509_t {
 };
 
 /**
+ * ASN.1 definition of generalName 
+ */
+static const asn1Object_t generalNameObjects[] = {
+       { 0,   "otherName",             ASN1_CONTEXT_C_0,  ASN1_OPT|ASN1_BODY   }, /*  0 */
+       { 0,   "end choice",    ASN1_EOC,          ASN1_END                             }, /*  1 */
+       { 0,   "rfc822Name",    ASN1_CONTEXT_S_1,  ASN1_OPT|ASN1_BODY   }, /*  2 */
+       { 0,   "end choice",    ASN1_EOC,          ASN1_END                     }, /*  3 */
+       { 0,   "dnsName",               ASN1_CONTEXT_S_2,  ASN1_OPT|ASN1_BODY   }, /*  4 */
+       { 0,   "end choice",    ASN1_EOC,          ASN1_END                             }, /*  5 */
+       { 0,   "x400Address",   ASN1_CONTEXT_S_3,  ASN1_OPT|ASN1_BODY   }, /*  6 */
+       { 0,   "end choice",    ASN1_EOC,          ASN1_END                             }, /*  7 */
+       { 0,   "directoryName", ASN1_CONTEXT_C_4,  ASN1_OPT|ASN1_BODY   }, /*  8 */
+       { 0,   "end choice",    ASN1_EOC,          ASN1_END                             }, /*  9 */
+       { 0,   "ediPartyName",  ASN1_CONTEXT_C_5,  ASN1_OPT|ASN1_BODY   }, /* 10 */
+       { 0,   "end choice",    ASN1_EOC,          ASN1_END                             }, /* 11 */
+       { 0,   "URI",                   ASN1_CONTEXT_S_6,  ASN1_OPT|ASN1_BODY   }, /* 12 */
+       { 0,   "end choice",    ASN1_EOC,          ASN1_END                             }, /* 13 */
+       { 0,   "ipAddress",             ASN1_CONTEXT_S_7,  ASN1_OPT|ASN1_BODY   }, /* 14 */
+       { 0,   "end choice",    ASN1_EOC,          ASN1_END                             }, /* 15 */
+       { 0,   "registeredID",  ASN1_CONTEXT_S_8,  ASN1_OPT|ASN1_BODY   }, /* 16 */
+       { 0,   "end choice",    ASN1_EOC,          ASN1_END                             }  /* 17 */
+};
+#define GN_OBJ_OTHER_NAME               0
+#define GN_OBJ_RFC822_NAME              2
+#define GN_OBJ_DNS_NAME                         4
+#define GN_OBJ_X400_ADDRESS             6
+#define GN_OBJ_DIRECTORY_NAME   8
+#define GN_OBJ_EDI_PARTY_NAME  10
+#define GN_OBJ_URI                             12
+#define GN_OBJ_IP_ADDRESS              14
+#define GN_OBJ_REGISTERED_ID   16
+#define GN_OBJ_ROOF                            18
+
+/**
+ * ASN.1 definition of otherName 
+ */
+static const asn1Object_t otherNameObjects[] = {
+       {0, "type-id",  ASN1_OID,                       ASN1_BODY       }, /*  0 */
+       {0, "value",    ASN1_CONTEXT_C_0,       ASN1_BODY       }  /*  1 */
+};
+#define ON_OBJ_ID_TYPE         0
+#define ON_OBJ_VALUE           1
+#define ON_OBJ_ROOF                    2
+/**
  * ASN.1 definition of a basicConstraints extension 
  */
 static const asn1Object_t basicConstraintsObjects[] = {
@@ -325,90 +371,140 @@ static const chunk_t ASN1_subjectAltName_oid = chunk_from_buf(ASN1_subjectAltNam
 /**
  * compare two X.509 x509s by comparing their signatures
  */
-static bool equals(private_x509_t *this, private_x509_t *other)
+static bool equals(const private_x509_t *this, const private_x509_t *other)
 {
        return chunk_equals(this->signature, other->signature);
 }
 
 /**
- * encode a linked list of subjectAltNames
+ * extracts the basicConstraints extension
  */
-chunk_t build_subjectAltNames(generalName_t *subjectAltNames)
+static bool parse_basicConstraints(chunk_t blob, int level0)
 {
-       u_char *pos;
-       chunk_t names;
-       size_t len = 0;
-       generalName_t *gn = subjectAltNames;
-       
-       /* compute the total size of the ASN.1 attributes object */
-       while (gn != NULL)
-       {
-               len += gn->name.len;
-               gn = gn->next;
-       }
+       asn1_ctx_t ctx;
+       chunk_t object;
+       u_int level;
+       int objectID = 0;
+       bool isCA = FALSE;
 
-       pos = build_asn1_object(&names, ASN1_SEQUENCE, len);
+       asn1_init(&ctx, blob, level0, FALSE);
 
-       gn = subjectAltNames;
-       while (gn != NULL)
-       {
-               memcpy(pos, gn->name.ptr, gn->name.len); 
-               pos += gn->name.len;
-               gn = gn->next;
-       }
+       while (objectID < BASIC_CONSTRAINTS_ROOF) {
 
-       return asn1_wrap(ASN1_SEQUENCE, "cm",
-                                        ASN1_subjectAltName_oid,
-                                        asn1_wrap(ASN1_OCTET_STRING, "m", names)
-                                       );
+               if (!extract_object(basicConstraintsObjects, &objectID, &object,&level, &ctx))
+               {
+                       break;
+               }
+               if (objectID == BASIC_CONSTRAINTS_CA)
+               {
+                       isCA = object.len && *object.ptr;
+                       logger->log(logger, CONTROL|LEVEL2, "  %s", isCA ? "TRUE" : "FALSE");
+               }
+               objectID++;
+       }
+       return isCA;
 }
 
-/**
- * free the dynamic memory used to store generalNames
+/*
+ * extracts an otherName
  */
-void free_generalNames(generalName_t* gn, bool free_name)
+static bool
+parse_otherName(chunk_t blob, int level0)
 {
-       while (gn != NULL)
+       asn1_ctx_t ctx;
+       chunk_t object;
+       int objectID = 0;
+       u_int level;
+       int oid = OID_UNKNOWN;
+
+       asn1_init(&ctx, blob, level0, FALSE);
+
+       while (objectID < ON_OBJ_ROOF)
        {
-               generalName_t *gn_top = gn;
-               if (free_name)
+               if (!extract_object(otherNameObjects, &objectID, &object, &level, &ctx))
+                       return FALSE;
+
+               switch (objectID)
                {
-                       free(gn->name.ptr);
+                       case ON_OBJ_ID_TYPE:
+                               oid = known_oid(object);
+                               break;
+                       case ON_OBJ_VALUE:
+                               if (oid == OID_XMPP_ADDR)
+                               {
+                                       if (!parse_asn1_simple_object(&object, ASN1_UTF8STRING, level + 1, "xmppAddr"))
+                                               return FALSE;
+                               }
+                               break;
+                       default:
+                               break;
                }
-               gn = gn->next;
-               free(gn_top);
+               objectID++;
        }
+       return TRUE;
 }
 
-/**
- * extracts the basicConstraints extension
+/*
+ * extracts a generalName
  */
-static bool parse_basicConstraints(chunk_t blob, int level0)
+static identification_t *parse_generalName(chunk_t blob, int level0)
 {
+       u_char buf[BUF_LEN];
        asn1_ctx_t ctx;
        chunk_t object;
-       u_int level;
        int objectID = 0;
-       bool isCA = FALSE;
+       u_int level;
 
        asn1_init(&ctx, blob, level0, FALSE);
 
-       while (objectID < BASIC_CONSTRAINTS_ROOF) {
+       while (objectID < GN_OBJ_ROOF)
+       {
+               id_type_t id_type = ID_ANY;
+       
+               if (!extract_object(generalNameObjects, &objectID, &object, &level, &ctx))
+                       return NULL;
 
-               if (!extract_object(basicConstraintsObjects, &objectID, &object,&level, &ctx))
+               switch (objectID)
                {
-                       break;
+                       case GN_OBJ_RFC822_NAME:
+                               id_type = ID_RFC822_ADDR;
+                               break;
+                       case GN_OBJ_DNS_NAME:
+                               id_type = ID_FQDN;
+                               break;
+                       case GN_OBJ_URI:
+                               id_type = ID_DER_ASN1_GN_URI;
+                               break;
+                       case GN_OBJ_DIRECTORY_NAME:
+                               id_type = ID_DER_ASN1_DN;
+                       break;
+                       case GN_OBJ_IP_ADDRESS:
+                               id_type = ID_IPV4_ADDR;
+                               break;
+                       case GN_OBJ_OTHER_NAME:
+                               if (!parse_otherName(object, level + 1))
+                                       return NULL;
+                               break;
+                       case GN_OBJ_X400_ADDRESS:
+                       case GN_OBJ_EDI_PARTY_NAME:
+                       case GN_OBJ_REGISTERED_ID:
+                               break;
+                       default:
+                               break;
                }
-               if (objectID == BASIC_CONSTRAINTS_CA)
+
+               if (id_type != ID_ANY)
                {
-                       isCA = object.len && *object.ptr;
-                       logger->log(logger, RAW|LEVEL1, "  %s", isCA ? "TRUE" : "FALSE");
-               }
+                       identification_t *gn = identification_create_from_encoding(id_type, object);
+                       logger->log(logger, CONTROL|LEVEL2, "  '%s'", gn->get_string(gn));
+                       return gn;
+        }
                objectID++;
-       }
-       return isCA;
+    }
+    return NULL;
 }
 
+
 /**
  * extracts one or several GNs and puts them into a chained list
  */
@@ -428,7 +524,10 @@ static void parse_generalNames(chunk_t blob, int level0, bool implicit, linked_l
                
                if (objectID == GENERAL_NAMES_GN)
                {
-                       list->insert_last(list, identification_create_from_encoding(ID_DER_ASN1_GN, object));
+                       identification_t *gn = parse_generalName(object, level+1);
+
+                       if (gn != NULL)
+                               list->insert_last(list, gn);
                }
                objectID++;
        }
@@ -547,10 +646,8 @@ static void parse_authorityInfoAccess(chunk_t blob, int level0, chunk_t *accessL
                                                if (*object.ptr == ASN1_CONTEXT_S_6)
                                                {
                                                        if (asn1_length(&object) == ASN1_INVALID_LENGTH)
-                                                       {
                                                                return;
-                                                       }
-                                                       logger->log(logger, RAW|LEVEL1, "  '%.*s'",(int)object.len, object.ptr);
+                                                       logger->log(logger, CONTROL|LEVEL2, "  '%.*s'",(int)object.len, object.ptr);
                                                        /* only HTTP(S) URIs accepted */
                                                        if (strncasecmp(object.ptr, "http", 4) == 0)
                                                        {
@@ -630,7 +727,7 @@ static void parse_crlDistributionPoints(chunk_t blob, int level0, linked_list_t
 
 
 /**
- * Parses an X.509v3 x509
+ * Parses an X.509v3 certificate
  */
 bool parse_x509cert(chunk_t blob, u_int level0, private_x509_t *cert)
 {
@@ -659,7 +756,7 @@ bool parse_x509cert(chunk_t blob, u_int level0, private_x509_t *cert)
                                break;
                        case X509_OBJ_VERSION:
                                cert->version = (object.len) ? (1+(u_int)*object.ptr) : 1;
-                               logger->log(logger, RAW|LEVEL1, "  v%d", cert->version);
+                               logger->log(logger, CONTROL|LEVEL2, "  v%d", cert->version);
                                break;
                        case X509_OBJ_SERIAL_NUMBER:
                                cert->serialNumber = object;
@@ -669,6 +766,7 @@ bool parse_x509cert(chunk_t blob, u_int level0, private_x509_t *cert)
                                break;
                        case X509_OBJ_ISSUER:
                                cert->issuer = identification_create_from_encoding(ID_DER_ASN1_DN, object);
+                               logger->log(logger, CONTROL|LEVEL1, "  '%s'", cert->issuer->get_string(cert->issuer));
                                break;
                        case X509_OBJ_NOT_BEFORE:
                                cert->notBefore = parse_time(object, level);
@@ -678,6 +776,7 @@ bool parse_x509cert(chunk_t blob, u_int level0, private_x509_t *cert)
                                break;
                        case X509_OBJ_SUBJECT:
                                cert->subject = identification_create_from_encoding(ID_DER_ASN1_DN, object);
+                               logger->log(logger, CONTROL|LEVEL1, "  '%s'", cert->subject->get_string(cert->subject));
                                break;
                        case X509_OBJ_SUBJECT_PUBLIC_KEY_ALGORITHM:
                                if (parse_algorithmIdentifier(object, level, NULL) != OID_RSA_ENCRYPTION)
@@ -706,7 +805,7 @@ bool parse_x509cert(chunk_t blob, u_int level0, private_x509_t *cert)
                                break;
                        case X509_OBJ_CRITICAL:
                                critical = object.len && *object.ptr;
-                               logger->log(logger, ERROR|LEVEL1, "  %s", critical ? "TRUE" : "FALSE");
+                               logger->log(logger, ERROR|LEVEL2, "  %s", critical ? "TRUE" : "FALSE");
                                break;
                        case X509_OBJ_EXTN_VALUE:
                        {
@@ -737,9 +836,7 @@ bool parse_x509cert(chunk_t blob, u_int level0, private_x509_t *cert)
                                        case OID_NS_CA_POLICY_URL:
                                        case OID_NS_COMMENT:
                                                if (!parse_asn1_simple_object(&object, ASN1_IA5STRING , level, oid_names[extn_oid].name))
-                                               {
                                                        return FALSE;
-                                               }
                                                break;
                                        default:
                                                break;
@@ -762,45 +859,77 @@ bool parse_x509cert(chunk_t blob, u_int level0, private_x509_t *cert)
 }
 
 /**
- * verify the validity of a x509 by
- * checking the notBefore and notAfter dates
+ * Implements x509_t.is_valid
  */
-err_t check_validity(const private_x509_t *cert, time_t *until)
+static err_t is_valid(const private_x509_t *this, time_t *until)
 {
-       time_t current_time;
-       
-       time(&current_time);
+       char buf[TIMETOA_BUF];
+
+       time_t current_time = time(NULL);
        
-       if (cert->notAfter < *until) 
+       timetoa(buf, BUF_LEN, &this->notBefore, TRUE);
+       logger->log(logger, CONTROL|LEVEL1, "  not before  : %s", buf);
+       timetoa(buf, BUF_LEN, &current_time, TRUE);
+       logger->log(logger, CONTROL|LEVEL1, "  current time: %s", buf);
+       timetoa(buf, BUF_LEN, &this->notAfter, TRUE);
+       logger->log(logger, CONTROL|LEVEL1, "  not after   : %s", buf);
+
+       if (until != NULL
+       && (*until == UNDEFINED_TIME || this->notAfter < *until)) 
        {
-               *until = cert->notAfter;
+               *until = this->notAfter;
        }
-       if (current_time < cert->notBefore)
-       {
-               return "x509 is not valid yet";
-       }
-       if (current_time > cert->notAfter)
-       {
-               return "x509 has expired";
-       }
-       else
+       if (current_time < this->notBefore)
+               return "is not valid yet";
+       if (current_time > this->notAfter)
+               return "has expired";
+       logger->log(logger, CONTROL|LEVEL1, "  certificate is valid", buf);
+       return NULL;
+}
+
+/**
+ * Implements x509_t.is_ca
+ */
+static bool is_ca(const private_x509_t *this)
+{
+       return this->isCA;
+}
+
+/**
+ * Implements x509_t.equals_subjectAltName
+ */
+static bool equals_subjectAltName(const private_x509_t *this, identification_t *id)
+{
+       bool found = FALSE;
+       iterator_t *iterator = this->subjectAltNames->create_iterator(this->subjectAltNames, TRUE);
+
+       while (iterator->has_next(iterator))
        {
-               return NULL;
+               identification_t *subjectAltName;
+
+               iterator->current(iterator, (void**)&subjectAltName);
+               if (id->equals(id, subjectAltName))
+               {
+                       found = TRUE;
+                       break;
+               }
        }
+       iterator->destroy(iterator);
+       return found;
 }
 
 /**
  * Implements x509_t.get_public_key
  */
-static rsa_public_key_t *get_public_key(private_x509_t *this)
+static rsa_public_key_t *get_public_key(const private_x509_t *this)
 {
-       return this->public_key->clone(this->public_key);;
+       return this->public_key->clone(this->public_key);
 }
 
 /**
  * Implements x509_t.get_subject
  */
-static identification_t *get_subject(private_x509_t *this)
+static identification_t *get_subject(const private_x509_t *this)
 {
        return this->subject;
 }
@@ -808,7 +937,7 @@ static identification_t *get_subject(private_x509_t *this)
 /**
  * Implements x509_t.get_issuer
  */
-static identification_t *get_issuer(private_x509_t *this)
+static identification_t *get_issuer(const private_x509_t *this)
 {
        return this->issuer;
 }
@@ -824,55 +953,104 @@ static void destroy(private_x509_t *this)
                id->destroy(id);
        }
        this->subjectAltNames->destroy(this->subjectAltNames);
-       while (this->issuerAltNames->remove_last(this->issuerAltNames, (void**)&id) == SUCCESS)
-       {
-               id->destroy(id);
-       }
-       this->issuerAltNames->destroy(this->issuerAltNames);
+
        while (this->crlDistributionPoints->remove_last(this->crlDistributionPoints, (void**)&id) == SUCCESS)
        {
                id->destroy(id);
        }
        this->crlDistributionPoints->destroy(this->crlDistributionPoints);
+
        if (this->issuer)
-       {
                this->issuer->destroy(this->issuer);
-       }
+
        if (this->subject)
-       {
                this->subject->destroy(this->subject);
-       }
+
        if (this->public_key)
-       {
                this->public_key->destroy(this->public_key);
-       }
+
        free(this->certificate.ptr);
        free(this);
 }
 
 /**
+ * checks if the expiration date has been reached and warns during the
+  * warning_interval of the imminent expiration.
+  * strict=TRUE declares a fatal error, strict=FALSE issues a warning upon expiry.
+ */
+char* check_expiry(time_t expiration_date, int warning_interval, bool strict)
+{
+       time_t now;
+       int time_left;
+
+       if (expiration_date == UNDEFINED_TIME)
+               return "ok (expires never)";
+
+       time_left = (expiration_date - time(NULL));
+       if (time_left < 0)
+               return strict? "fatal (expired)" : "warning (expired)";
+
+       {
+               static char buf[35]; /* temporary storage */
+               const char* unit = "second";
+
+               if (time_left > 86400*warning_interval)
+                       return "ok";
+
+               if (time_left > 172800)
+               {
+                       time_left /= 86400;
+                       unit = "day";
+               }
+               else if (time_left > 7200)
+               {
+                       time_left /= 3600;
+                       unit = "hour";
+               }
+               else if (time_left > 120)
+               {
+                       time_left /= 60;
+                       unit = "minute";
+               }
+               snprintf(buf, sizeof(buf), "warning (expires in %d %s%s)", time_left, unit, (time_left == 1)?"":"s");
+       }
+}
+
+/**
  * log certificate
  */
-static void log_certificate(private_x509_t *this, logger_t *logger, bool utc)
+static void log_certificate(const private_x509_t *this, logger_t *logger, bool utc, bool has_key)
 {
        identification_t *subject = this->subject;
        identification_t *issuer = this->issuer;
-
-       rsa_public_key_t *rsa_key = this->public_key;
+       rsa_public_key_t *pubkey = this->public_key;
 
        char buf[BUF_LEN];
 
+    /* determine the current time */
+    time_t now = time(NULL);
+
        timetoa(buf, BUF_LEN, &this->installed, utc);
        logger->log(logger, CONTROL, "%s", buf);
        logger->log(logger, CONTROL, "       subject: '%s'", subject->get_string(subject));
        logger->log(logger, CONTROL, "       issuer:  '%s'", issuer->get_string(issuer));
+       
        chunk_to_hex(buf, BUF_LEN, this->serialNumber);
        logger->log(logger, CONTROL, "       serial:   %s", buf);
+       
        timetoa(buf, BUF_LEN, &this->notBefore, utc);
-       logger->log(logger, CONTROL, "       validity: not before %s", buf);
+       logger->log(logger, CONTROL, "       validity: not before %s %s", buf,
+                               (this->notBefore < now)? "ok":"fatal (not valid yet)");
+       
        timetoa(buf, BUF_LEN, &this->notAfter, utc);
-       logger->log(logger, CONTROL, "                 not after  %s", buf);
-       logger->log(logger, CONTROL, "       pubkey:   RSA %d bits", BITS_PER_BYTE * rsa_key->get_keysize(rsa_key));
+       logger->log(logger, CONTROL, "                 not after  %s %s", buf,
+                       check_expiry(this->notAfter, CERT_WARNING_INTERVAL, TRUE));
+
+       logger->log(logger, CONTROL, "       pubkey:   RSA %d bits%s",
+                       BITS_PER_BYTE * pubkey->get_keysize(pubkey), has_key? ", has private key":"");
+       chunk_to_hex(buf, BUF_LEN, pubkey->get_keyid(pubkey));
+       logger->log(logger, CONTROL, "       keyid:    %s", buf);
+
        if (this->subjectKeyID.ptr != NULL)
        {
                chunk_to_hex(buf, BUF_LEN, this->subjectKeyID);
@@ -897,62 +1075,63 @@ x509_t *x509_create_from_chunk(chunk_t chunk)
 {
        private_x509_t *this = malloc_thing(private_x509_t);
        
-       /* public functions */
-       this->public.equals = (bool (*) (x509_t*,x509_t*))equals;
-       this->public.destroy = (void (*) (x509_t*))destroy;
-       this->public.get_public_key = (rsa_public_key_t* (*) (x509_t*))get_public_key;
-       this->public.get_subject = (identification_t* (*) (x509_t*))get_subject;
-       this->public.get_issuer = (identification_t* (*) (x509_t*))get_issuer;
-       this->public.log_certificate = (void (*) (x509_t*,logger_t*,bool))log_certificate;
-       
        /* initialize */
        this->subjectPublicKey = CHUNK_INITIALIZER;
        this->public_key = NULL;
        this->subject = NULL;
        this->issuer = NULL;
        this->subjectAltNames = linked_list_create();
-       this->issuerAltNames = linked_list_create();
        this->crlDistributionPoints = linked_list_create();
        this->subjectKeyID = CHUNK_INITIALIZER;
        this->authKeyID = CHUNK_INITIALIZER;
        this->authKeySerialNumber = CHUNK_INITIALIZER;
        
+       /* public functions */
+       this->public.equals = (bool (*) (const x509_t*,const x509_t*))equals;
+       this->public.equals_subjectAltName = (bool (*) (const x509_t*,identification_t*))equals_subjectAltName;
+       this->public.is_valid = (err_t (*) (const x509_t*,time_t*))is_valid;
+       this->public.is_ca = (bool (*) (const x509_t*))is_ca;
+       this->public.destroy = (void (*) (x509_t*))destroy;
+       this->public.get_public_key = (rsa_public_key_t* (*) (const x509_t*))get_public_key;
+       this->public.get_subject = (identification_t* (*) (const x509_t*))get_subject;
+       this->public.get_issuer = (identification_t* (*) (const x509_t*))get_issuer;
+       this->public.log_certificate = (void (*) (const x509_t*,logger_t*,bool,bool))log_certificate;
+
        /* we do not use a per-instance logger right now, since its not always accessible */
        logger = logger_manager->get_logger(logger_manager, ASN1);
        
-       if (!is_asn1(chunk) ||
-               !parse_x509cert(chunk, 0, this))
+       if (!parse_x509cert(chunk, 0, this))
        {
                destroy(this);
                return NULL;
        }
-       
+
+       /* extract public key from certificate */
        this->public_key = rsa_public_key_create_from_chunk(this->subjectPublicKey);
        if (this->public_key == NULL)
        {
                destroy(this);
                return NULL;
        }
-       
+
        return &this->public;
 }
 
 /*
  * Described in header.
  */
-x509_t *x509_create_from_file(const char *filename)
+x509_t *x509_create_from_file(const char *filename, const char *label)
 {
        bool pgp = FALSE;
        chunk_t chunk = CHUNK_INITIALIZER;
        x509_t *cert = NULL;
 
-       if (!pem_asn1_load_file(filename, "", "certificate", &chunk, &pgp))
+       if (!pem_asn1_load_file(filename, "", label, &chunk, &pgp))
                return NULL;
 
        cert = x509_create_from_chunk(chunk);
+
        if (cert == NULL)
-       {
                free(chunk.ptr);
-       }
        return cert;
 }