removed TODO reminder
[strongswan.git] / src / libstrongswan / plugins / x509 / x509_cert.c
index 6a0be36..b8df5ad 100644 (file)
@@ -4,6 +4,7 @@
  * Copyright (C) 2002 Mario Strasser
  * Copyright (C) 2000-2006 Andreas Steffen
  * Copyright (C) 2006-2008 Martin Willi
+ * Copyright (C) 2008 Tobias Brunner
  * Hochschule fuer Technik Rapperswil
  *
  * This program is free software; you can redistribute it and/or modify it
  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
  * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  * for more details.
- *
- * $Id$
  */
 
 #define _GNU_SOURCE
 
 #include "x509_cert.h"
 
-#include <gmp.h>
 #include <sys/stat.h>
+#include <time.h>
 #include <unistd.h>
 #include <string.h>
 #include <stdio.h>
 
-#include <crypto/hashers/hasher.h>
 #include <library.h>
 #include <debug.h>
 #include <asn1/oid.h>
 #include <asn1/asn1.h>
-#include <asn1/pem.h>
+#include <asn1/asn1_parser.h>
+#include <crypto/hashers/hasher.h>
+#include <credentials/keys/private_key.h>
 #include <utils/linked_list.h>
 #include <utils/identification.h>
 
@@ -64,12 +64,17 @@ struct private_x509_cert_t {
         * Public interface for this certificate.
         */
        x509_cert_t public;
-
+       
        /**
-        * DER encoded X.509 certificate
+        * X.509 certificate encoding in ASN.1 DER format
         */
-       chunk_t certificate;
-
+       chunk_t encoding;
+       
+       /**
+        * SHA1 hash of the DER encoding of this X.509 certificate
+        */
+       chunk_t encoding_hash;
+       
        /**
         * X.509 certificate body over which signature is computed
         */
@@ -89,17 +94,17 @@ struct private_x509_cert_t {
         * ID representing the certificate issuer
         */
        identification_t *issuer;
-
+       
        /**
         * Start time of certificate validity
         */
        time_t notBefore;
-
+       
        /**
         * End time of certificate validity
         */
        time_t notAfter;
-
+       
        /**
         * ID representing the certificate subject
         */
@@ -114,12 +119,12 @@ struct private_x509_cert_t {
         * List of crlDistributionPoints as allocated char*
         */
        linked_list_t *crl_uris;
-
+       
        /**
         * List ocspAccessLocations as identification_t
         */
        linked_list_t *ocsp_uris;
-
+       
        /**
         * certificates embedded public key
         */
@@ -129,12 +134,12 @@ struct private_x509_cert_t {
         * Subject Key Identifier
         */
        chunk_t subjectKeyID;
-
+       
        /**
         * Authority Key Identifier
         */
-       identification_t *authKeyIdentifier;
-
+       chunk_t authKeyIdentifier;
+       
        /**
         * Authority Key Serial Number
         */
@@ -144,69 +149,32 @@ struct private_x509_cert_t {
         * x509 constraints and other flags
         */
        x509_flag_t flags;
-
+       
        /**
         * Signature algorithm
         */
        int algorithm;
-
+       
        /**
         * Signature
         */
        chunk_t signature;
        
        /**
+        * Certificate parsed from blob/file?
+        */
+       bool parsed;
+       
+       /**
         * reference count
         */
        refcount_t ref;
 };
 
-/**
- * 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 */
+static u_char ASN1_sAN_oid_buf[] = {
+       0x06, 0x03, 0x55, 0x1D, 0x11
 };
-
-#define ON_OBJ_ID_TYPE         0
-#define ON_OBJ_VALUE           1
-#define ON_OBJ_ROOF                    2
+static const chunk_t ASN1_subjectAltName_oid = chunk_from_buf(ASN1_sAN_oid_buf);
 
 /**
  * ASN.1 definition of a basicConstraints extension 
@@ -215,246 +183,142 @@ static const asn1Object_t basicConstraintsObjects[] = {
        { 0, "basicConstraints",        ASN1_SEQUENCE,  ASN1_NONE                       }, /*  0 */
        { 1,   "CA",                            ASN1_BOOLEAN,   ASN1_DEF|ASN1_BODY      }, /*  1 */
        { 1,   "pathLenConstraint",     ASN1_INTEGER,   ASN1_OPT|ASN1_BODY      }, /*  2 */
-       { 1,   "end opt",                       ASN1_EOC,               ASN1_END                        }  /*  3 */
+       { 1,   "end opt",                       ASN1_EOC,               ASN1_END                        }, /*  3 */
+       { 0, "exit",                            ASN1_EOC,               ASN1_EXIT                       }
 };
-
 #define BASIC_CONSTRAINTS_CA   1
-#define BASIC_CONSTRAINTS_ROOF 4
-
-/** 
- * ASN.1 definition of a keyIdentifier 
- */
-static const asn1Object_t keyIdentifierObjects[] = {
-       { 0,   "keyIdentifier", ASN1_OCTET_STRING,      ASN1_BODY }  /*  0 */
-};
-
-/**
- * ASN.1 definition of a authorityKeyIdentifier extension 
- */
-static const asn1Object_t authorityKeyIdentifierObjects[] = {
-       { 0,   "authorityKeyIdentifier",        ASN1_SEQUENCE,          ASN1_NONE                       }, /*  0 */
-       { 1,     "keyIdentifier",                       ASN1_CONTEXT_S_0,       ASN1_OPT|ASN1_OBJ       }, /*  1 */
-       { 1,     "end opt",                                     ASN1_EOC,                       ASN1_END                        }, /*  2 */
-       { 1,     "authorityCertIssuer",         ASN1_CONTEXT_C_1,       ASN1_OPT|ASN1_OBJ       }, /*  3 */
-       { 1,     "end opt",                                     ASN1_EOC,                       ASN1_END                        }, /*  4 */
-       { 1,     "authorityCertSerialNumber",ASN1_CONTEXT_S_2,  ASN1_OPT|ASN1_BODY      }, /*  5 */
-       { 1,     "end opt",                                     ASN1_EOC,                       ASN1_END                        }  /*  6 */
-};
-
-#define AUTH_KEY_ID_KEY_ID                     1
-#define AUTH_KEY_ID_CERT_ISSUER                3
-#define AUTH_KEY_ID_CERT_SERIAL                5
-#define AUTH_KEY_ID_ROOF                       7
-
-/**
- * ASN.1 definition of a authorityInfoAccess extension 
- */
-static const asn1Object_t authorityInfoAccessObjects[] = {
-       { 0,   "authorityInfoAccess",   ASN1_SEQUENCE,  ASN1_LOOP }, /*  0 */
-       { 1,     "accessDescription",   ASN1_SEQUENCE,  ASN1_NONE }, /*  1 */
-       { 2,       "accessMethod",              ASN1_OID,               ASN1_BODY }, /*  2 */
-       { 2,       "accessLocation",    ASN1_EOC,               ASN1_RAW  }, /*  3 */
-       { 0,   "end loop",                              ASN1_EOC,               ASN1_END  }  /*  4 */
-};
-
-#define AUTH_INFO_ACCESS_METHOD                2
-#define AUTH_INFO_ACCESS_LOCATION      3
-#define AUTH_INFO_ACCESS_ROOF          5
-
-/**
- * ASN.1 definition of a extendedKeyUsage extension
- */
-static const asn1Object_t extendedKeyUsageObjects[] = {
-       { 0, "extendedKeyUsage",        ASN1_SEQUENCE,  ASN1_LOOP }, /*  0 */
-       { 1,   "keyPurposeID",          ASN1_OID,               ASN1_BODY }, /*  1 */
-       { 0, "end loop",                        ASN1_EOC,               ASN1_END  }, /*  2 */
-};
-
-#define EXT_KEY_USAGE_PURPOSE_ID       1
-#define EXT_KEY_USAGE_ROOF                     3
-
-/**
- * ASN.1 definition of generalNames 
- */
-static const asn1Object_t generalNamesObjects[] = {
-       { 0, "generalNames",    ASN1_SEQUENCE,  ASN1_LOOP }, /*  0 */
-       { 1,   "generalName",   ASN1_EOC,               ASN1_RAW  }, /*  1 */
-       { 0, "end loop",                ASN1_EOC,               ASN1_END  }  /*  2 */
-};
-
-#define GENERAL_NAMES_GN       1
-#define GENERAL_NAMES_ROOF     3
-
-
-/**
- * ASN.1 definition of crlDistributionPoints
- */
-static const asn1Object_t crlDistributionPointsObjects[] = {
-       { 0, "crlDistributionPoints",   ASN1_SEQUENCE,          ASN1_LOOP                       }, /*  0 */
-       { 1,   "DistributionPoint",             ASN1_SEQUENCE,          ASN1_NONE                       }, /*  1 */
-       { 2,     "distributionPoint",   ASN1_CONTEXT_C_0,       ASN1_OPT|ASN1_LOOP      }, /*  2 */
-       { 3,       "fullName",                  ASN1_CONTEXT_C_0,       ASN1_OPT|ASN1_OBJ       }, /*  3 */
-       { 3,       "end choice",                ASN1_EOC,                       ASN1_END                        }, /*  4 */
-       { 3,       "nameRelToCRLIssuer",ASN1_CONTEXT_C_1,       ASN1_OPT|ASN1_BODY      }, /*  5 */
-       { 3,       "end choice",                ASN1_EOC,                       ASN1_END                        }, /*  6 */
-       { 2,     "end opt",                             ASN1_EOC,                       ASN1_END                        }, /*  7 */
-       { 2,     "reasons",                             ASN1_CONTEXT_C_1,       ASN1_OPT|ASN1_BODY      }, /*  8 */
-       { 2,     "end opt",                             ASN1_EOC,                       ASN1_END                        }, /*  9 */
-       { 2,     "crlIssuer",                   ASN1_CONTEXT_C_2,       ASN1_OPT|ASN1_BODY      }, /* 10 */
-       { 2,     "end opt",                             ASN1_EOC,                       ASN1_END                        }, /* 11 */
-       { 0, "end loop",                                ASN1_EOC,                       ASN1_END                        }, /* 12 */
-};
-
-#define CRL_DIST_POINTS_FULLNAME        3
-#define CRL_DIST_POINTS_ROOF           13
 
 /**
- * ASN.1 definition of an X.509v3 x509_cert
- */
-static const asn1Object_t certObjects[] = {
-       { 0, "x509",                                    ASN1_SEQUENCE,          ASN1_OBJ                        }, /*  0 */
-       { 1,   "tbsCertificate",                ASN1_SEQUENCE,          ASN1_OBJ                        }, /*  1 */
-       { 2,     "DEFAULT v1",                  ASN1_CONTEXT_C_0,       ASN1_DEF                        }, /*  2 */
-       { 3,       "version",                   ASN1_INTEGER,           ASN1_BODY                       }, /*  3 */
-       { 2,     "serialNumber",                ASN1_INTEGER,           ASN1_BODY                       }, /*  4 */
-       { 2,     "signature",                   ASN1_EOC,                       ASN1_RAW                        }, /*  5 */
-       { 2,     "issuer",                              ASN1_SEQUENCE,          ASN1_OBJ                        }, /*  6 */
-       { 2,     "validity",                    ASN1_SEQUENCE,          ASN1_NONE                       }, /*  7 */
-       { 3,       "notBefore",                 ASN1_EOC,                       ASN1_RAW                        }, /*  8 */
-       { 3,       "notAfter",                  ASN1_EOC,                       ASN1_RAW                        }, /*  9 */
-       { 2,     "subject",                             ASN1_SEQUENCE,          ASN1_OBJ                        }, /* 10 */
-       { 2,     "subjectPublicKeyInfo",ASN1_SEQUENCE,          ASN1_NONE                       }, /* 11 */
-       { 3,       "algorithm",                 ASN1_EOC,                       ASN1_RAW                        }, /* 12 */
-       { 3,       "subjectPublicKey",  ASN1_BIT_STRING,        ASN1_NONE                       }, /* 13 */
-       { 4,         "RSAPublicKey",    ASN1_SEQUENCE,          ASN1_RAW                        }, /* 14 */
-       { 2,     "issuerUniqueID",              ASN1_CONTEXT_C_1,       ASN1_OPT                        }, /* 15 */
-       { 2,     "end opt",                             ASN1_EOC,                       ASN1_END                        }, /* 16 */
-       { 2,     "subjectUniqueID",             ASN1_CONTEXT_C_2,       ASN1_OPT                        }, /* 17 */
-       { 2,     "end opt",                             ASN1_EOC,                       ASN1_END                        }, /* 18 */
-       { 2,     "optional extensions", ASN1_CONTEXT_C_3,       ASN1_OPT                        }, /* 19 */
-       { 3,       "extensions",                ASN1_SEQUENCE,          ASN1_LOOP                       }, /* 20 */
-       { 4,         "extension",               ASN1_SEQUENCE,          ASN1_NONE                       }, /* 21 */
-       { 5,           "extnID",                ASN1_OID,                       ASN1_BODY                       }, /* 22 */
-       { 5,           "critical",              ASN1_BOOLEAN,           ASN1_DEF|ASN1_BODY      }, /* 23 */
-       { 5,           "extnValue",             ASN1_OCTET_STRING,      ASN1_BODY                       }, /* 24 */
-       { 3,       "end loop",                  ASN1_EOC,                       ASN1_END                        }, /* 25 */
-       { 2,     "end opt",                             ASN1_EOC,                       ASN1_END                        }, /* 26 */
-       { 1,   "signatureAlgorithm",    ASN1_EOC,                       ASN1_RAW                        }, /* 27 */
-       { 1,   "signatureValue",                ASN1_BIT_STRING,        ASN1_BODY                       }  /* 28 */
-};
-
-#define X509_OBJ_TBS_CERTIFICATE                                1
-#define X509_OBJ_VERSION                                                3
-#define X509_OBJ_SERIAL_NUMBER                                  4
-#define X509_OBJ_SIG_ALG                                                5
-#define X509_OBJ_ISSUER                                                 6
-#define X509_OBJ_NOT_BEFORE                                             8
-#define X509_OBJ_NOT_AFTER                                              9
-#define X509_OBJ_SUBJECT                                               10
-#define X509_OBJ_SUBJECT_PUBLIC_KEY_ALGORITHM  12
-#define X509_OBJ_SUBJECT_PUBLIC_KEY                            13
-#define X509_OBJ_RSA_PUBLIC_KEY                                        14
-#define X509_OBJ_EXTN_ID                                               22
-#define X509_OBJ_CRITICAL                                              23
-#define X509_OBJ_EXTN_VALUE                                            24
-#define X509_OBJ_ALGORITHM                                             27
-#define X509_OBJ_SIGNATURE                                             28
-#define X509_OBJ_ROOF                                                  29
-
-
-static u_char ASN1_sAN_oid_buf[] = {
-       0x06, 0x03, 0x55, 0x1D, 0x11
-};
-static const chunk_t ASN1_subjectAltName_oid = chunk_from_buf(ASN1_sAN_oid_buf);
-
-/**
- * extracts the basicConstraints extension
+ * Extracts the basicConstraints extension
  */
 static bool parse_basicConstraints(chunk_t blob, int level0)
 {
-       asn1_ctx_t ctx;
+       asn1_parser_t *parser;
        chunk_t object;
-       u_int level;
-       int objectID = 0;
+       int objectID;
        bool isCA = FALSE;
 
-       asn1_init(&ctx, blob, level0, FALSE, FALSE);
-       while (objectID < BASIC_CONSTRAINTS_ROOF) {
+       parser = asn1_parser_create(basicConstraintsObjects, blob);
+       parser->set_top_level(parser, level0);
 
-               if (!extract_object(basicConstraintsObjects, &objectID, &object,&level, &ctx))
-               {
-                       break;
-               }
+       while (parser->iterate(parser, &objectID, &object))
+       {
                if (objectID == BASIC_CONSTRAINTS_CA)
                {
                        isCA = object.len && *object.ptr;
                        DBG2("  %s", isCA ? "TRUE" : "FALSE");
                }
-               objectID++;
        }
+       parser->destroy(parser);
+
        return isCA;
 }
 
-/*
- * extracts an otherName
+/**
+ * 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 */
+       {0, "exit",             ASN1_EOC,                       ASN1_EXIT       }
+};
+#define ON_OBJ_ID_TYPE         0
+#define ON_OBJ_VALUE           1
+
+/**
+ * Extracts an otherName
  */
 static bool parse_otherName(chunk_t blob, int level0)
 {
-       asn1_ctx_t ctx;
+       asn1_parser_t *parser;
        chunk_t object;
-       u_int level;
-       int objectID = 0;
+       int objectID;
        int oid = OID_UNKNOWN;
+       bool success = FALSE;
+
+       parser = asn1_parser_create(otherNameObjects, blob);
+       parser->set_top_level(parser, level0);
 
-       asn1_init(&ctx, blob, level0, FALSE, FALSE);
-       while (objectID < ON_OBJ_ROOF)
+       while (parser->iterate(parser, &objectID, &object))
        {
-               if (!extract_object(otherNameObjects, &objectID, &object, &level, &ctx))
-               {
-                       return FALSE;
-               }
                switch (objectID)
                {
                        case ON_OBJ_ID_TYPE:
-                               oid = known_oid(object);
+                               oid = asn1_known_oid(object);
                                break;
                        case ON_OBJ_VALUE:
                                if (oid == OID_XMPP_ADDR)
                                {
-                                       if (!parse_asn1_simple_object(&object, ASN1_UTF8STRING,
-                                                                                                 level + 1, "xmppAddr"))
+                                       if (!asn1_parse_simple_object(&object, ASN1_UTF8STRING,
+                                                               parser->get_level(parser)+1, "xmppAddr"))
                                        {
-                                               return FALSE;
+                                               goto end;
                                        }
                                }
                                break;
                        default:
                                break;
                }
-               objectID++;
        }
-       return TRUE;
+       success = parser->success(parser);
+       
+end:
+       parser->destroy(parser);
+       return success;
 }
 
-/*
- * extracts a generalName
+/**
+ * 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 */
+       { 0, "exit",                    ASN1_EOC,          ASN1_EXIT                    }
+};
+#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
+
+/**
+ * Extracts a generalName
  */
 static identification_t *parse_generalName(chunk_t blob, int level0)
 {
-       asn1_ctx_t ctx;
+       asn1_parser_t *parser;
        chunk_t object;
-       int objectID = 0;
-       u_int level;
-
-       asn1_init(&ctx, blob, level0, FALSE, FALSE);
-       while (objectID < GN_OBJ_ROOF)
+       int objectID ;
+       
+       identification_t *gn = NULL;
+       
+       parser = asn1_parser_create(generalNameObjects, blob);
+       parser->set_top_level(parser, level0);
+       
+       while (parser->iterate(parser, &objectID, &object))
        {
                id_type_t id_type = ID_ANY;
-       
-               if (!extract_object(generalNameObjects, &objectID, &object, &level, &ctx))
-               {
-                       return NULL;
-               }
+               
                switch (objectID)
                {
                        case GN_OBJ_RFC822_NAME:
@@ -468,156 +332,163 @@ static identification_t *parse_generalName(chunk_t blob, int level0)
                                break;
                        case GN_OBJ_DIRECTORY_NAME:
                                id_type = ID_DER_ASN1_DN;
-                       break;
+                               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;
+                               if (!parse_otherName(object, parser->get_level(parser)+1))
+                               {
+                                       goto end;
+                               }
                                break;
                        case GN_OBJ_X400_ADDRESS:
                        case GN_OBJ_EDI_PARTY_NAME:
                        case GN_OBJ_REGISTERED_ID:
-                               break;
                        default:
                                break;
                }
                if (id_type != ID_ANY)
                {
-                       identification_t *gn = identification_create_from_encoding(id_type, object);
-                       DBG2("  '%D'", gn);
-                       return gn;
-        }
-               objectID++;
-    }
-    return NULL;
+                       gn = identification_create_from_encoding(id_type, object);
+                       DBG2("  '%Y'", gn);
+                       goto end;
+               }
+       }
+       
+end:
+       parser->destroy(parser);
+       return gn;
 }
 
+/**
+ * ASN.1 definition of generalNames 
+ */
+static const asn1Object_t generalNamesObjects[] = {
+       { 0, "generalNames",    ASN1_SEQUENCE,  ASN1_LOOP }, /* 0 */
+       { 1,   "generalName",   ASN1_EOC,               ASN1_RAW  }, /* 1 */
+       { 0, "end loop",                ASN1_EOC,               ASN1_END  }, /* 2 */
+       { 0, "exit",                    ASN1_EOC,               ASN1_EXIT }
+};
+#define GENERAL_NAMES_GN       1
 
 /**
- * extracts one or several GNs and puts them into a chained list
+ * Extracts one or several GNs and puts them into a chained list
  */
-void parse_generalNames(chunk_t blob, int level0, bool implicit, linked_list_t *list)
+void x509_parse_generalNames(chunk_t blob, int level0, bool implicit, linked_list_t *list)
 {
-       asn1_ctx_t ctx;
+       asn1_parser_t *parser;
        chunk_t object;
-       u_int level;
-       int objectID = 0;
-
-       asn1_init(&ctx, blob, level0, implicit, FALSE);
-       while (objectID < GENERAL_NAMES_ROOF)
+       int objectID;
+       
+       parser = asn1_parser_create(generalNamesObjects, blob);
+       parser->set_top_level(parser, level0);
+       parser->set_flags(parser, implicit, FALSE);
+       
+       while (parser->iterate(parser, &objectID, &object))
        {
-               if (!extract_object(generalNamesObjects, &objectID, &object, &level, &ctx))
-               {
-                       return;
-               }
                if (objectID == GENERAL_NAMES_GN)
                {
-                       identification_t *gn = parse_generalName(object, level+1);
-
-                       if (gn != NULL)
+                       identification_t *gn = parse_generalName(object,
+                                                                                       parser->get_level(parser)+1);
+                       
+                       if (gn)
                        {
                                list->insert_last(list, (void *)gn);
                        }
                }
-               objectID++;
        }
-       return;
+       parser->destroy(parser);
 }
 
 /**
- * extracts a keyIdentifier
+ * ASN.1 definition of a authorityKeyIdentifier extension 
  */
-static chunk_t parse_keyIdentifier(chunk_t blob, int level0, bool implicit)
-{
-       asn1_ctx_t ctx;
-       chunk_t object;
-       u_int level;
-       int objectID = 0;
-       
-       asn1_init(&ctx, blob, level0, implicit, FALSE);
-       if (!extract_object(keyIdentifierObjects, &objectID, &object, &level, &ctx))
-       {
-               return chunk_empty;
-       }
-       return object;
-}
+static const asn1Object_t authKeyIdentifierObjects[] = {
+       { 0, "authorityKeyIdentifier",          ASN1_SEQUENCE,          ASN1_NONE                       }, /* 0 */
+       { 1,   "keyIdentifier",                         ASN1_CONTEXT_S_0,       ASN1_OPT|ASN1_BODY      }, /* 1 */
+       { 1,   "end opt",                                       ASN1_EOC,                       ASN1_END                        }, /* 2 */
+       { 1,   "authorityCertIssuer",           ASN1_CONTEXT_C_1,       ASN1_OPT|ASN1_OBJ       }, /* 3 */
+       { 1,   "end opt",                                       ASN1_EOC,                       ASN1_END                        }, /* 4 */
+       { 1,   "authorityCertSerialNumber",     ASN1_CONTEXT_S_2,       ASN1_OPT|ASN1_BODY      }, /* 5 */
+       { 1,   "end opt",                                       ASN1_EOC,                       ASN1_END                        }, /* 6 */
+       { 0, "exit",                                            ASN1_EOC,                       ASN1_EXIT                       }
+};
+#define AUTH_KEY_ID_KEY_ID                     1
+#define AUTH_KEY_ID_CERT_ISSUER                3
+#define AUTH_KEY_ID_CERT_SERIAL                5
 
 /**
- * extracts an authoritykeyIdentifier
+ * Extracts an authoritykeyIdentifier
  */
-identification_t* x509_parse_authorityKeyIdentifier(chunk_t blob, int level0,
+chunk_t x509_parse_authorityKeyIdentifier(chunk_t blob, int level0,
                                                                                                chunk_t *authKeySerialNumber)
 {
-       asn1_ctx_t ctx;
+       asn1_parser_t *parser;
        chunk_t object;
-       u_int level;
-       int objectID = 0;
-       identification_t *authKeyIdentifier = NULL;
-
+       int objectID;
+       chunk_t authKeyIdentifier = chunk_empty;
+       
        *authKeySerialNumber = chunk_empty;
-
-       asn1_init(&ctx, blob, level0, FALSE, FALSE);
-       while (objectID < AUTH_KEY_ID_ROOF)
+       
+       parser = asn1_parser_create(authKeyIdentifierObjects, blob);
+       parser->set_top_level(parser, level0);
+       
+       while (parser->iterate(parser, &objectID, &object))
        {
-               if (!extract_object(authorityKeyIdentifierObjects, &objectID, &object, &level, &ctx))
-               {
-                       return NULL;
-               }
                switch (objectID) 
                {
                        case AUTH_KEY_ID_KEY_ID:
-                       {
-                               chunk_t authKeyID = parse_keyIdentifier(object, level+1, TRUE);
-
-                               if (authKeyID.ptr == NULL)
-                               {
-                                       return NULL;
-                               }
-                               authKeyIdentifier = identification_create_from_encoding(
-                                                                                       ID_PUBKEY_SHA1, authKeyID); 
+                               authKeyIdentifier = chunk_clone(object);
                                break;
-                       }
                        case AUTH_KEY_ID_CERT_ISSUER:
-                       {
-                               /* TODO: parse_generalNames(object, level+1, TRUE); */
+                               /* TODO: x509_parse_generalNames(object, level+1, TRUE); */
                                break;
-                       }
                        case AUTH_KEY_ID_CERT_SERIAL:
                                *authKeySerialNumber = object;
                                break;
                        default:
                                break;
                }
-               objectID++;
        }
+       parser->destroy(parser);
        return authKeyIdentifier;
 }
 
 /**
- * extracts an authorityInfoAcess location
+ * ASN.1 definition of a authorityInfoAccess extension 
+ */
+static const asn1Object_t authInfoAccessObjects[] = {
+       { 0, "authorityInfoAccess",     ASN1_SEQUENCE,  ASN1_LOOP }, /* 0 */
+       { 1,   "accessDescription",     ASN1_SEQUENCE,  ASN1_NONE }, /* 1 */
+       { 2,     "accessMethod",        ASN1_OID,               ASN1_BODY }, /* 2 */
+       { 2,     "accessLocation",      ASN1_EOC,               ASN1_RAW  }, /* 3 */
+       { 0, "end loop",                        ASN1_EOC,               ASN1_END  }, /* 4 */
+       { 0, "exit",                            ASN1_EOC,               ASN1_EXIT }
+};
+#define AUTH_INFO_ACCESS_METHOD                2
+#define AUTH_INFO_ACCESS_LOCATION      3
+
+/**
+ * Extracts an authorityInfoAcess location
  */
 static void parse_authorityInfoAccess(chunk_t blob, int level0,
                                                                          private_x509_cert_t *this)
 {
-       asn1_ctx_t ctx;
+       asn1_parser_t *parser;
        chunk_t object;
-       u_int level;
-       int objectID = 0;
+       int objectID;
        int accessMethod = OID_UNKNOWN;
        
-       asn1_init(&ctx, blob, level0, FALSE, FALSE);
-       while (objectID < AUTH_INFO_ACCESS_ROOF)
+       parser = asn1_parser_create(authInfoAccessObjects, blob);
+       parser->set_top_level(parser, level0);
+       
+       while (parser->iterate(parser, &objectID, &object))
        {
-               if (!extract_object(authorityInfoAccessObjects, &objectID, &object, &level, &ctx))
-               {
-                       return;
-               }
                switch (objectID) 
                {
                        case AUTH_INFO_ACCESS_METHOD:
-                               accessMethod = known_oid(object);
+                               accessMethod = asn1_known_oid(object);
                                break;
                        case AUTH_INFO_ACCESS_LOCATION:
                        {
@@ -628,15 +499,17 @@ static void parse_authorityInfoAccess(chunk_t blob, int level0,
                                                {
                                                        identification_t *id;
                                                        char *uri;
-
-                                                       id = parse_generalName(object, level+1);
+                                                       
+                                                       id = parse_generalName(object,
+                                                                                       parser->get_level(parser)+1);
                                                        if (id == NULL)
-                                                       {       /* parsing went wrong - abort */
-                                                               return;
+                                                       {
+                                                               /* parsing went wrong - abort */
+                                                               goto end;
                                                        }
-                                                       DBG2("  '%D'", id);
+                                                       DBG2("  '%Y'", id);
                                                        if (accessMethod == OID_OCSP &&
-                                                               asprintf(&uri, "%D", id) > 0)
+                                                               asprintf(&uri, "%Y", id) > 0)
                                                        {
                                                                this->ocsp_uris->insert_last(this->ocsp_uris, uri);
                                                        }
@@ -652,102 +525,175 @@ static void parse_authorityInfoAccess(chunk_t blob, int level0,
                        default:
                                break;
                }
-               objectID++;
        }
+       
+end:
+       parser->destroy(parser);
 }
 
 /**
- * extracts extendedKeyUsage OIDs
+ * ASN.1 definition of a extendedKeyUsage extension
+ */
+static const asn1Object_t extendedKeyUsageObjects[] = {
+       { 0, "extendedKeyUsage",        ASN1_SEQUENCE,  ASN1_LOOP }, /* 0 */
+       { 1,   "keyPurposeID",          ASN1_OID,               ASN1_BODY }, /* 1 */
+       { 0, "end loop",                        ASN1_EOC,               ASN1_END  }, /* 2 */
+       { 0, "exit",                            ASN1_EOC,               ASN1_EXIT }
+};
+#define EXT_KEY_USAGE_PURPOSE_ID       1
+
+/**
+ * Extracts extendedKeyUsage OIDs - currently only OCSP_SIGING is returned
  */
 static bool parse_extendedKeyUsage(chunk_t blob, int level0)
 {
-       asn1_ctx_t ctx;
+       asn1_parser_t *parser;
        chunk_t object;
-       u_int level;
-       int objectID = 0;
+       int objectID;
+       bool ocsp_signing = FALSE;
+       
+       parser = asn1_parser_create(extendedKeyUsageObjects, blob);
+       parser->set_top_level(parser, level0);
        
-       asn1_init(&ctx, blob, level0, FALSE, FALSE);
-       while (objectID < EXT_KEY_USAGE_ROOF)
+       while (parser->iterate(parser, &objectID, &object))
        {
-               if (!extract_object(extendedKeyUsageObjects, &objectID, &object, &level, &ctx))
-               {
-                       return FALSE;
-               }
                if (objectID == EXT_KEY_USAGE_PURPOSE_ID && 
-                       known_oid(object) == OID_OCSP_SIGNING)
+                       asn1_known_oid(object) == OID_OCSP_SIGNING)
                {
-                       return TRUE;
+                       ocsp_signing = TRUE;
                }
-               objectID++;
        }
-       return FALSE;
+       parser->destroy(parser);
+       return ocsp_signing;
 }
 
 /**
- * extracts one or several crlDistributionPoints into a list
+ * ASN.1 definition of crlDistributionPoints
+ */
+static const asn1Object_t crlDistributionPointsObjects[] = {
+       { 0, "crlDistributionPoints",   ASN1_SEQUENCE,          ASN1_LOOP                       }, /*  0 */
+       { 1,   "DistributionPoint",             ASN1_SEQUENCE,          ASN1_NONE                       }, /*  1 */
+       { 2,     "distributionPoint",   ASN1_CONTEXT_C_0,       ASN1_OPT|ASN1_LOOP      }, /*  2 */
+       { 3,       "fullName",                  ASN1_CONTEXT_C_0,       ASN1_OPT|ASN1_OBJ       }, /*  3 */
+       { 3,       "end choice",                ASN1_EOC,                       ASN1_END                        }, /*  4 */
+       { 3,       "nameRelToCRLIssuer",ASN1_CONTEXT_C_1,       ASN1_OPT|ASN1_BODY      }, /*  5 */
+       { 3,       "end choice",                ASN1_EOC,                       ASN1_END                        }, /*  6 */
+       { 2,     "end opt",                             ASN1_EOC,                       ASN1_END                        }, /*  7 */
+       { 2,     "reasons",                             ASN1_CONTEXT_C_1,       ASN1_OPT|ASN1_BODY      }, /*  8 */
+       { 2,     "end opt",                             ASN1_EOC,                       ASN1_END                        }, /*  9 */
+       { 2,     "crlIssuer",                   ASN1_CONTEXT_C_2,       ASN1_OPT|ASN1_BODY      }, /* 10 */
+       { 2,     "end opt",                             ASN1_EOC,                       ASN1_END                        }, /* 11 */
+       { 0, "end loop",                                ASN1_EOC,                       ASN1_END                        }, /* 12 */
+       { 0, "exit",                                    ASN1_EOC,                       ASN1_EXIT                       }
+};
+#define CRL_DIST_POINTS_FULLNAME        3
+
+/**
+ * Extracts one or several crlDistributionPoints into a list
  */
 static void parse_crlDistributionPoints(chunk_t blob, int level0,
                                                                                private_x509_cert_t *this)
 {
-       asn1_ctx_t ctx;
+       asn1_parser_t *parser;
        chunk_t object;
-       u_int level;
-       int objectID = 0;
-       linked_list_t *list;
-       identification_t *id;
-       char *uri;
-       
-       list = linked_list_create();
-       asn1_init(&ctx, blob, level0, FALSE, FALSE);
-       while (objectID < CRL_DIST_POINTS_ROOF)
+       int objectID;
+       linked_list_t *list = linked_list_create();
+       
+       parser = asn1_parser_create(crlDistributionPointsObjects, blob);
+       parser->set_top_level(parser, level0);
+       
+       while (parser->iterate(parser, &objectID, &object))
        {
-               if (!extract_object(crlDistributionPointsObjects, &objectID, &object, &level, &ctx))
-               {
-                       list->destroy_offset(list, offsetof(identification_t, destroy));
-                       return;
-               }
                if (objectID == CRL_DIST_POINTS_FULLNAME)
-               {       /* append extracted generalNames to existing chained list */
-                       parse_generalNames(object, level+1, TRUE, list);
+               {
+                       identification_t *id;
+                       
+                       /* append extracted generalNames to existing chained list */
+                       x509_parse_generalNames(object, parser->get_level(parser)+1,
+                                                                       TRUE, list);
        
                        while (list->remove_last(list, (void**)&id) == SUCCESS)
                        {
-                               if (asprintf(&uri, "%D", id) > 0)
+                               char *uri;
+                               
+                               if (asprintf(&uri, "%Y", id) > 0)
                                {
                                        this->crl_uris->insert_last(this->crl_uris, uri);
                                }
                                id->destroy(id);
                        }
                }
-               objectID++;
        }
+       parser->destroy(parser);
        list->destroy(list);
 }
 
 /**
+ * ASN.1 definition of an X.509v3 x509_cert
+ */
+static const asn1Object_t certObjects[] = {
+       { 0, "x509",                                    ASN1_SEQUENCE,          ASN1_OBJ                        }, /*  0 */
+       { 1,   "tbsCertificate",                ASN1_SEQUENCE,          ASN1_OBJ                        }, /*  1 */
+       { 2,     "DEFAULT v1",                  ASN1_CONTEXT_C_0,       ASN1_DEF                        }, /*  2 */
+       { 3,       "version",                   ASN1_INTEGER,           ASN1_BODY                       }, /*  3 */
+       { 2,     "serialNumber",                ASN1_INTEGER,           ASN1_BODY                       }, /*  4 */
+       { 2,     "signature",                   ASN1_EOC,                       ASN1_RAW                        }, /*  5 */
+       { 2,     "issuer",                              ASN1_SEQUENCE,          ASN1_OBJ                        }, /*  6 */
+       { 2,     "validity",                    ASN1_SEQUENCE,          ASN1_NONE                       }, /*  7 */
+       { 3,       "notBefore",                 ASN1_EOC,                       ASN1_RAW                        }, /*  8 */
+       { 3,       "notAfter",                  ASN1_EOC,                       ASN1_RAW                        }, /*  9 */
+       { 2,     "subject",                             ASN1_SEQUENCE,          ASN1_OBJ                        }, /* 10 */
+       { 2,     "subjectPublicKeyInfo",ASN1_SEQUENCE,          ASN1_RAW                        }, /* 11 */
+       { 2,     "issuerUniqueID",              ASN1_CONTEXT_C_1,       ASN1_OPT                        }, /* 12 */
+       { 2,     "end opt",                             ASN1_EOC,                       ASN1_END                        }, /* 13 */
+       { 2,     "subjectUniqueID",             ASN1_CONTEXT_C_2,       ASN1_OPT                        }, /* 14 */
+       { 2,     "end opt",                             ASN1_EOC,                       ASN1_END                        }, /* 15 */
+       { 2,     "optional extensions", ASN1_CONTEXT_C_3,       ASN1_OPT                        }, /* 16 */
+       { 3,       "extensions",                ASN1_SEQUENCE,          ASN1_LOOP                       }, /* 17 */
+       { 4,         "extension",               ASN1_SEQUENCE,          ASN1_NONE                       }, /* 18 */
+       { 5,           "extnID",                ASN1_OID,                       ASN1_BODY                       }, /* 19 */
+       { 5,           "critical",              ASN1_BOOLEAN,           ASN1_DEF|ASN1_BODY      }, /* 20 */
+       { 5,           "extnValue",             ASN1_OCTET_STRING,      ASN1_BODY                       }, /* 21 */
+       { 3,       "end loop",                  ASN1_EOC,                       ASN1_END                        }, /* 22 */
+       { 2,     "end opt",                             ASN1_EOC,                       ASN1_END                        }, /* 23 */
+       { 1,   "signatureAlgorithm",    ASN1_EOC,                       ASN1_RAW                        }, /* 24 */
+       { 1,   "signatureValue",                ASN1_BIT_STRING,        ASN1_BODY                       }, /* 25 */
+       { 0, "exit",                                    ASN1_EOC,                       ASN1_EXIT                       }
+};
+#define X509_OBJ_TBS_CERTIFICATE                                1
+#define X509_OBJ_VERSION                                                3
+#define X509_OBJ_SERIAL_NUMBER                                  4
+#define X509_OBJ_SIG_ALG                                                5
+#define X509_OBJ_ISSUER                                                 6
+#define X509_OBJ_NOT_BEFORE                                             8
+#define X509_OBJ_NOT_AFTER                                              9
+#define X509_OBJ_SUBJECT                                               10
+#define X509_OBJ_SUBJECT_PUBLIC_KEY_INFO               11
+#define X509_OBJ_EXTN_ID                                               19
+#define X509_OBJ_CRITICAL                                              20
+#define X509_OBJ_EXTN_VALUE                                            21
+#define X509_OBJ_ALGORITHM                                             24
+#define X509_OBJ_SIGNATURE                                             25
+
+/**
  * Parses an X.509v3 certificate
  */
 static bool parse_certificate(private_x509_cert_t *this)
 {
-       asn1_ctx_t ctx;
-       bool critical;
+       asn1_parser_t *parser;
        chunk_t object;
-       u_int level;
-       int objectID = 0;
+       int objectID;
        int extn_oid = OID_UNKNOWN;
-       int key_alg = 0;
-       int sig_alg = 0;
-       chunk_t subjectPublicKey = chunk_empty;
+       int sig_alg  = OID_UNKNOWN;
+       bool success = FALSE;
+       bool critical;
        
-       asn1_init(&ctx, this->certificate, 0, FALSE, FALSE);
-       while (objectID < X509_OBJ_ROOF)
+       parser = asn1_parser_create(certObjects, this->encoding);
+       
+       while (parser->iterate(parser, &objectID, &object))
        {
-               if (!extract_object(certObjects, &objectID, &object, &level, &ctx))
-               {
-                       return FALSE;
-               }
-               /* those objects which will parsed further need the next higher level */
-               level++;
+               u_int level = parser->get_level(parser)+1;
+               
                switch (objectID)
                {
                        case X509_OBJ_TBS_CERTIFICATE:
@@ -761,49 +707,32 @@ static bool parse_certificate(private_x509_cert_t *this)
                                this->serialNumber = object;
                                break;
                        case X509_OBJ_SIG_ALG:
-                               sig_alg = parse_algorithmIdentifier(object, level, NULL);
+                               sig_alg = asn1_parse_algorithmIdentifier(object, level, NULL);
                                break;
                        case X509_OBJ_ISSUER:
                                this->issuer = identification_create_from_encoding(ID_DER_ASN1_DN, object);
-                               DBG2("  '%D'", this->issuer);
+                               DBG2("  '%Y'", this->issuer);
                                break;
                        case X509_OBJ_NOT_BEFORE:
-                               this->notBefore = parse_time(object, level);
+                               this->notBefore = asn1_parse_time(object, level);
                                break;
                        case X509_OBJ_NOT_AFTER:
-                               this->notAfter = parse_time(object, level);
+                               this->notAfter = asn1_parse_time(object, level);
                                break;
                        case X509_OBJ_SUBJECT:
                                this->subject = identification_create_from_encoding(ID_DER_ASN1_DN, object);
-                               DBG2("  '%D'", this->subject);
-                               break;
-                       case X509_OBJ_SUBJECT_PUBLIC_KEY_ALGORITHM:
-                               key_alg = parse_algorithmIdentifier(object, level, NULL);
+                               DBG2("  '%Y'", this->subject);
                                break;
-                       case X509_OBJ_SUBJECT_PUBLIC_KEY:
-                               if (ctx.blobs[4].len > 0 && *ctx.blobs[4].ptr == 0x00)
+                       case X509_OBJ_SUBJECT_PUBLIC_KEY_INFO:
+                               this->public_key = lib->creds->create(lib->creds, CRED_PUBLIC_KEY,
+                                               KEY_ANY, BUILD_BLOB_ASN1_DER, object, BUILD_END);
+                               if (this->public_key == NULL)
                                {
-                                       /* skip initial bit string octet defining 0 unused bits */
-                                       ctx.blobs[4].ptr++; ctx.blobs[4].len--;
-                               }
-                               break;
-                       case X509_OBJ_RSA_PUBLIC_KEY:
-                               subjectPublicKey = object;
-                               switch (key_alg)
-                               {
-                                       case OID_RSA_ENCRYPTION:
-                                               this->public_key = lib->creds->create(lib->creds,
-                                                       CRED_PUBLIC_KEY, KEY_RSA,
-                                                       BUILD_BLOB_ASN1_DER, chunk_clone(subjectPublicKey),
-                                                       BUILD_END);
-                                               break;
-                                       default:
-                                               DBG1("parsing key type %d failed", key_alg);
-                                               return FALSE;
+                                       goto end;
                                }
                                break;
                        case X509_OBJ_EXTN_ID:
-                               extn_oid = known_oid(object);
+                               extn_oid = asn1_known_oid(object);
                                break;
                        case X509_OBJ_CRITICAL:
                                critical = object.len && *object.ptr;
@@ -814,10 +743,16 @@ static bool parse_certificate(private_x509_cert_t *this)
                                switch (extn_oid)
                                {
                                        case OID_SUBJECT_KEY_ID:
-                                               this->subjectKeyID = parse_keyIdentifier(object, level, FALSE);
+                                               if (!asn1_parse_simple_object(&object, ASN1_OCTET_STRING,
+                                                                                                         level, "keyIdentifier"))
+                                               {
+                                                       goto end;
+                                               }
+                                               this->subjectKeyID = object;
                                                break;
                                        case OID_SUBJECT_ALT_NAME:
-                                               parse_generalNames(object, level, FALSE, this->subjectAltNames);
+                                               x509_parse_generalNames(object, level, FALSE,
+                                                                                               this->subjectAltNames);
                                                break;
                                        case OID_BASIC_CONSTRAINTS:
                                                if (parse_basicConstraints(object, level))
@@ -845,9 +780,11 @@ static bool parse_certificate(private_x509_cert_t *this)
                                        case OID_NS_CA_REVOCATION_URL:
                                        case OID_NS_CA_POLICY_URL:
                                        case OID_NS_COMMENT:
-                                               if (!parse_asn1_simple_object(&object, ASN1_IA5STRING, 
+                                               if (!asn1_parse_simple_object(&object, ASN1_IA5STRING, 
                                                                                        level, oid_names[extn_oid].name))
-                                                       return FALSE;
+                                               {
+                                                       goto end;
+                                               }
                                                break;
                                        default:
                                                break;
@@ -855,11 +792,11 @@ static bool parse_certificate(private_x509_cert_t *this)
                                break;
                        }
                        case X509_OBJ_ALGORITHM:
-                               this->algorithm = parse_algorithmIdentifier(object, level, NULL);
+                               this->algorithm = asn1_parse_algorithmIdentifier(object, level, NULL);
                                if (this->algorithm != sig_alg)
                                {
                                        DBG1("  signature algorithms do not agree");
-                                       return FALSE;
+                                       goto end;
                                }
                                break;
                        case X509_OBJ_SIGNATURE:
@@ -868,9 +805,12 @@ static bool parse_certificate(private_x509_cert_t *this)
                        default:
                                break;
                }
-               objectID++;
        }
-       return TRUE;
+       success = parser->success(parser);
+       
+end:
+       parser->destroy(parser);
+       return success;
 }
 
 /**
@@ -905,7 +845,15 @@ static id_match_t has_subject(private_x509_cert_t *this, identification_t *subje
        identification_t *current;
        enumerator_t *enumerator;
        id_match_t match, best;
-
+       
+       if (this->encoding_hash.ptr && subject->get_type(subject) == ID_KEY_ID)
+       {
+               if (chunk_equals(this->encoding_hash, subject->get_encoding(subject)))
+               {
+                       return ID_MATCH_PERFECT;
+               }
+       }
+       
        best = this->subject->matches(this->subject, subject);
        enumerator = this->subjectAltNames->create_enumerator(this->subjectAltNames);
        while (enumerator->enumerate(enumerator, &current))
@@ -913,11 +861,11 @@ static id_match_t has_subject(private_x509_cert_t *this, identification_t *subje
                match = current->matches(current, subject);
                if (match > best)
                {
-                       best = match;   
+                       best = match;
                }
        }
        enumerator->destroy(enumerator);
-       return best;    
+       return best;
 }
 
 /**
@@ -932,8 +880,7 @@ static id_match_t has_issuer(private_x509_cert_t *this, identification_t *issuer
 /**
  * Implementation of certificate_t.issued_by
  */
-static bool issued_by(private_x509_cert_t *this, certificate_t *issuer,
-                                         bool sigcheck)
+static bool issued_by(private_x509_cert_t *this, certificate_t *issuer)
 {
        public_key_t *key;
        signature_scheme_t scheme;
@@ -962,33 +909,14 @@ static bool issued_by(private_x509_cert_t *this, certificate_t *issuer,
        {
                return FALSE;
        }
-       if (!sigcheck)
-       {
-               return TRUE;
-       }
-       /* TODO: generic OID to scheme mapper? */
-       switch (this->algorithm)
-       {
-               case OID_MD5_WITH_RSA:
-                       scheme = SIGN_RSA_EMSA_PKCS1_MD5;
-                       break;
-               case OID_SHA1_WITH_RSA:
-                       scheme = SIGN_RSA_EMSA_PKCS1_SHA1;
-                       break;
-               case OID_SHA256_WITH_RSA:
-                       scheme = SIGN_RSA_EMSA_PKCS1_SHA256;
-                       break;
-               case OID_SHA384_WITH_RSA:
-                       scheme = SIGN_RSA_EMSA_PKCS1_SHA384;
-                       break;
-               case OID_SHA512_WITH_RSA:
-                       scheme = SIGN_RSA_EMSA_PKCS1_SHA512;
-                       break;
-               default:
-                       return FALSE;
-       }
+
+       /* get the public key of the issuer */
        key = issuer->get_public_key(issuer);
-       if (key == NULL)
+
+       /* determine signature scheme */
+       scheme = signature_scheme_from_oid(this->algorithm);
+
+       if (scheme == SIGN_UNKNOWN || key == NULL)
        {
                return FALSE;
        }
@@ -1058,11 +986,11 @@ static bool is_newer(certificate_t *this, certificate_t *that)
 {
        time_t this_update, that_update, now = time(NULL);
        bool new;
-
+       
        this->get_validity(this, &now, &this_update, NULL);
        that->get_validity(that, &now, &that_update, NULL);
        new = this_update > that_update;
-       DBG1("  certificate from %#T is %s - existing certificate from %#T %s",
+       DBG1("  certificate from %T is %s - existing certificate from %T %s",
                                &this_update, FALSE, new ? "newer":"not newer",
                                &that_update, FALSE, new ? "replaced":"retained");
        return new;
@@ -1073,7 +1001,7 @@ static bool is_newer(certificate_t *this, certificate_t *that)
  */
 static chunk_t get_encoding(private_x509_cert_t *this)
 {
-       return chunk_clone(this->certificate);
+       return chunk_clone(this->encoding);
 }
 
 /**
@@ -1081,6 +1009,9 @@ static chunk_t get_encoding(private_x509_cert_t *this)
  */
 static bool equals(private_x509_cert_t *this, certificate_t *other)
 {
+       chunk_t encoding;
+       bool equal;
+       
        if (this == (private_x509_cert_t*)other)
        {
                return TRUE;
@@ -1089,17 +1020,14 @@ static bool equals(private_x509_cert_t *this, certificate_t *other)
        {
                return FALSE;
        }
-       /* check if we have the same X509 implementation */
        if (other->equals == (void*)equals)
-       {
-               if (this->signature.len == 0)
-               {
-                       return FALSE;
-               }
-               return chunk_equals(this->signature, ((private_x509_cert_t*)other)->signature); 
+       {       /* skip allocation if we have the same implementation */
+               return chunk_equals(this->encoding, ((private_x509_cert_t*)other)->encoding); 
        }
-       /* TODO: compare against other implementation */
-       return FALSE;
+       encoding = other->get_encoding(other);
+       equal = chunk_equals(this->encoding, encoding);
+       free(encoding.ptr);
+       return equal;
 }
 
 /**
@@ -1113,7 +1041,7 @@ static chunk_t get_serial(private_x509_cert_t *this)
 /**
  * Implementation of x509_t.get_authKeyIdentifier.
  */
-static identification_t *get_authKeyIdentifier(private_x509_cert_t *this)
+static chunk_t get_authKeyIdentifier(private_x509_cert_t *this)
 {
        return this->authKeyIdentifier;
 }
@@ -1156,40 +1084,53 @@ static void destroy(private_x509_cert_t *this)
                DESTROY_IF(this->issuer);
                DESTROY_IF(this->subject);
                DESTROY_IF(this->public_key);
-               DESTROY_IF(this->authKeyIdentifier);
-               chunk_free(&this->certificate);
+               chunk_free(&this->authKeyIdentifier);
+               chunk_free(&this->encoding);
+               chunk_free(&this->encoding_hash);
+               if (!this->parsed)
+               {       /* only parsed certificates point these fields to "encoded" */
+                       chunk_free(&this->signature);
+                       chunk_free(&this->serialNumber);
+                       chunk_free(&this->tbsCertificate);
+               }
                free(this);
        }
 }
 
 /**
- * load x509 certificate from a chunk
+ * create an empty but initialized X.509 certificate
  */
-static private_x509_cert_t *load(chunk_t chunk)
+static private_x509_cert_t* create_empty(void)
 {
        private_x509_cert_t *this = malloc_thing(private_x509_cert_t);
        
-       this->public.interface.interface.get_type = (certificate_type_t (*)(certificate_t *this))get_type;
-       this->public.interface.interface.get_subject = (identification_t* (*)(certificate_t *this))get_subject;
-       this->public.interface.interface.get_issuer = (identification_t* (*)(certificate_t *this))get_issuer;
-       this->public.interface.interface.has_subject = (id_match_t (*)(certificate_t*, identification_t *subject))has_subject;
-       this->public.interface.interface.has_issuer = (id_match_t (*)(certificate_t*, identification_t *issuer))has_issuer;
-       this->public.interface.interface.issued_by = (bool (*)(certificate_t *this, certificate_t *issuer,bool))issued_by;
-       this->public.interface.interface.get_public_key = (public_key_t* (*)(certificate_t *this))get_public_key;
-       this->public.interface.interface.get_validity = (bool (*)(certificate_t*, time_t *when, time_t *, time_t*))get_validity;
-       this->public.interface.interface.is_newer = (bool (*)(certificate_t*,certificate_t*))is_newer;
-       this->public.interface.interface.get_encoding = (chunk_t (*)(certificate_t*))get_encoding;
-       this->public.interface.interface.equals = (bool (*)(certificate_t*, certificate_t *other))equals;
-       this->public.interface.interface.get_ref = (certificate_t* (*)(certificate_t *this))get_ref;
-       this->public.interface.interface.destroy = (void (*)(certificate_t *this))destroy;
+       this->public.interface.interface.get_type = (certificate_type_t (*) (certificate_t*))get_type;
+       this->public.interface.interface.get_subject = (identification_t* (*) (certificate_t*))get_subject;
+       this->public.interface.interface.get_issuer = (identification_t* (*) (certificate_t*))get_issuer;
+       this->public.interface.interface.has_subject = (id_match_t (*) (certificate_t*, identification_t*))has_subject;
+       this->public.interface.interface.has_issuer = (id_match_t (*) (certificate_t*, identification_t*))has_issuer;
+       this->public.interface.interface.issued_by = (bool (*) (certificate_t*, certificate_t*))issued_by;
+       this->public.interface.interface.get_public_key = (public_key_t* (*) (certificate_t*))get_public_key;
+       this->public.interface.interface.get_validity = (bool (*) (certificate_t*, time_t*, time_t*, time_t*))get_validity;
+       this->public.interface.interface.is_newer = (bool (*) (certificate_t*,certificate_t*))is_newer;
+       this->public.interface.interface.get_encoding = (chunk_t (*) (certificate_t*))get_encoding;
+       this->public.interface.interface.equals = (bool (*)(certificate_t*, certificate_t*))equals;
+       this->public.interface.interface.get_ref = (certificate_t* (*)(certificate_t*))get_ref;
+       this->public.interface.interface.destroy = (void (*)(certificate_t*))destroy;
        this->public.interface.get_flags = (x509_flag_t (*)(x509_t*))get_flags;
        this->public.interface.get_serial = (chunk_t (*)(x509_t*))get_serial;
-       this->public.interface.get_authKeyIdentifier = (identification_t* (*)(x509_t*))get_authKeyIdentifier;
+       this->public.interface.get_authKeyIdentifier = (chunk_t (*)(x509_t*))get_authKeyIdentifier;
        this->public.interface.create_subjectAltName_enumerator = (enumerator_t* (*)(x509_t*))create_subjectAltName_enumerator;
        this->public.interface.create_crl_uri_enumerator = (enumerator_t* (*)(x509_t*))create_crl_uri_enumerator;
        this->public.interface.create_ocsp_uri_enumerator = (enumerator_t* (*)(x509_t*))create_ocsp_uri_enumerator;
-
-       this->certificate = chunk;
+       
+       this->encoding = chunk_empty;
+       this->encoding_hash = chunk_empty;
+       this->tbsCertificate = chunk_empty;
+       this->version = 3;
+       this->serialNumber = chunk_empty;       
+       this->notBefore = 0;
+       this->notAfter = 0;
        this->public_key = NULL;
        this->subject = NULL;
        this->issuer = NULL;
@@ -1197,11 +1138,27 @@ static private_x509_cert_t *load(chunk_t chunk)
        this->crl_uris = linked_list_create();
        this->ocsp_uris = linked_list_create();
        this->subjectKeyID = chunk_empty;
-       this->authKeyIdentifier = NULL;
+       this->authKeyIdentifier = chunk_empty;
        this->authKeySerialNumber = chunk_empty;
+       this->algorithm = 0;
+       this->signature = chunk_empty;
        this->flags = 0;
        this->ref = 1;
+       this->parsed = FALSE;
        
+       return this;
+}
+
+/**
+ * create an X.509 certificate from a chunk
+ */
+static private_x509_cert_t *create_from_chunk(chunk_t chunk)
+{
+       hasher_t *hasher;
+       private_x509_cert_t *this = create_empty();
+       
+       this->encoding = chunk;
+       this->parsed = TRUE;
        if (!parse_certificate(this))
        {
                destroy(this);
@@ -1209,10 +1166,21 @@ static private_x509_cert_t *load(chunk_t chunk)
        }
        
        /* check if the certificate is self-signed */
-       if (issued_by(this, &this->public.interface.interface, TRUE))
+       if (issued_by(this, &this->public.interface.interface))
        {
                this->flags |= X509_SELF_SIGNED;
        }
+       
+       hasher = lib->crypto->create_hasher(lib->crypto, HASH_SHA1);
+       if (hasher == NULL)
+       {
+               DBG1("  unable to create hash of certificate, SHA1 not supported");     
+               destroy(this);
+               return NULL;    
+       }
+       hasher->allocate_hash(hasher, this->encoding, &this->encoding_hash);
+       hasher->destroy(hasher);
+       
        return this;
 }
 
@@ -1227,19 +1195,170 @@ struct private_builder_t {
        private_x509_cert_t *cert;
        /** additional flags to enforce */
        x509_flag_t flags;
+       /** certificate to sign, if we generate a new cert */
+       certificate_t *sign_cert;
+       /** private key to sign, if we generate a new cert */
+       private_key_t *sign_key;
+       /** digest algorithm to be used for signature */
+       hash_algorithm_t digest_alg;
 };
 
 /**
+ * Generate and sign a new certificate
+ */
+static bool generate(private_builder_t *this)
+{
+       chunk_t extensions = chunk_empty;
+       identification_t *issuer, *subject;
+       chunk_t key_info;
+       signature_scheme_t scheme;
+       hasher_t *hasher;
+       
+       subject = this->cert->subject;
+       if (this->sign_cert)
+       {
+               issuer = this->sign_cert->get_subject(this->sign_cert);
+               if (!this->cert->public_key)
+               {
+                       return FALSE;
+               }
+       }
+       else
+       {       /* self signed */
+               issuer = subject;
+               if (!this->cert->public_key)
+               {
+                       this->cert->public_key = this->sign_key->get_public_key(this->sign_key);
+               }
+               this->flags |= X509_SELF_SIGNED;
+       }
+       this->cert->issuer = issuer->clone(issuer);
+       if (!this->cert->notBefore)
+       {
+               this->cert->notBefore = time(NULL);
+       }
+       if (!this->cert->notAfter)
+       {       /* defaults to 1 year from now */
+               this->cert->notAfter = this->cert->notBefore + 60 * 60 * 24 * 365;
+       }
+       this->cert->flags = this->flags;
+       
+       switch (this->sign_key->get_type(this->sign_key))
+       {
+               case KEY_RSA:
+                       switch (this->digest_alg)
+                       {
+                               case HASH_MD5:
+                                       this->cert->algorithm = OID_MD5_WITH_RSA;
+                                       break;
+                               case HASH_SHA1:
+                                       this->cert->algorithm = OID_SHA1_WITH_RSA;
+                                       break;
+                               case HASH_SHA224:
+                                       this->cert->algorithm = OID_SHA224_WITH_RSA;
+                                       break;
+                               case HASH_SHA256:
+                                       this->cert->algorithm = OID_SHA256_WITH_RSA;
+                                       break;
+                               case HASH_SHA384:
+                                       this->cert->algorithm = OID_SHA384_WITH_RSA;
+                                       break;
+                               case HASH_SHA512:
+                                       this->cert->algorithm = OID_SHA512_WITH_RSA;
+                                       break;
+                               default:
+                                       return FALSE;
+                       }
+                       break;
+               case KEY_ECDSA:
+                       switch (this->digest_alg)
+                       {
+                               case HASH_SHA1:
+                                       this->cert->algorithm = OID_ECDSA_WITH_SHA1;
+                                       break;
+                               case HASH_SHA256:
+                                       this->cert->algorithm = OID_ECDSA_WITH_SHA256;
+                                       break;
+                               case HASH_SHA384:
+                                       this->cert->algorithm = OID_ECDSA_WITH_SHA384;
+                                       break;
+                               case HASH_SHA512:
+                                       this->cert->algorithm = OID_ECDSA_WITH_SHA512;
+                                       break;
+                               default:
+                                       return FALSE;
+                       }
+                       break;
+               default:
+                       return FALSE;
+       }
+       scheme = signature_scheme_from_oid(this->cert->algorithm);
+
+       if (!this->cert->public_key->get_encoding(this->cert->public_key,
+                                                                                         KEY_PUB_SPKI_ASN1_DER, &key_info))
+       {
+               return FALSE;
+       }
+       if (this->cert->subjectAltNames->get_count(this->cert->subjectAltNames))
+       {
+               /* TODO: encode subjectAltNames */
+       }
+       
+       this->cert->tbsCertificate = asn1_wrap(ASN1_SEQUENCE, "mmmcmcmm", 
+               asn1_simple_object(ASN1_CONTEXT_C_0, ASN1_INTEGER_2),
+               asn1_integer("c", this->cert->serialNumber),
+               asn1_algorithmIdentifier(this->cert->algorithm),
+               issuer->get_encoding(issuer),
+               asn1_wrap(ASN1_SEQUENCE, "mm",
+                       asn1_from_time(&this->cert->notBefore, ASN1_UTCTIME),
+                       asn1_from_time(&this->cert->notAfter, ASN1_UTCTIME)),
+               subject->get_encoding(subject),
+               key_info, extensions);
+       
+       if (!this->sign_key->sign(this->sign_key, scheme, 
+                                                       this->cert->tbsCertificate, &this->cert->signature))
+       {
+               return FALSE;
+       }
+       this->cert->encoding = asn1_wrap(ASN1_SEQUENCE, "cmm",
+                                                               this->cert->tbsCertificate,
+                                                               asn1_algorithmIdentifier(this->cert->algorithm),
+                                                               asn1_bitstring("c", this->cert->signature));
+       
+       hasher = lib->crypto->create_hasher(lib->crypto, HASH_SHA1);
+       if (!hasher)
+       {
+               return FALSE;
+       }
+       hasher->allocate_hash(hasher, this->cert->encoding,
+                                                 &this->cert->encoding_hash);
+       hasher->destroy(hasher);
+       return TRUE;
+}
+
+/**
  * Implementation of builder_t.build
  */
-static x509_cert_t *build(private_builder_t *this)
+static private_x509_cert_t *build(private_builder_t *this)
 {
        private_x509_cert_t *cert;
        
+       if (this->cert)
+       {
+               this->cert->flags |= this->flags;
+               if (!this->cert->encoding.ptr)
+               {       /* generate a new certificate */
+                       if (!this->sign_key || !generate(this))
+                       {
+                               destroy(this->cert);
+                               free(this);
+                               return NULL;
+                       }
+               }
+       }
        cert = this->cert;
-       cert->flags |= this->flags;
        free(this);
-       return &cert->public;
+       return cert;
 }
 
 /**
@@ -1248,26 +1367,83 @@ static x509_cert_t *build(private_builder_t *this)
 static void add(private_builder_t *this, builder_part_t part, ...)
 {
        va_list args;
+       chunk_t chunk;
+       bool handled = TRUE;
        
        va_start(args, part);
        switch (part)
        {
                case BUILD_BLOB_ASN1_DER:
-               {
-                       if (this->cert)
+                       chunk = va_arg(args, chunk_t);
+                       this->cert = create_from_chunk(chunk_clone(chunk));
+                       break;
+               case BUILD_X509_FLAG:
+                       this->flags = va_arg(args, x509_flag_t);
+                       break;
+               case BUILD_SIGNING_KEY:
+                       this->sign_key = va_arg(args, private_key_t*);
+                       break;
+               case BUILD_SIGNING_CERT:
+                       this->sign_cert = va_arg(args, certificate_t*);
+                       break;
+               default:
+                       /* all other parts need an empty cert */
+                       if (!this->cert)
                        {
-                               destroy(this->cert);
+                               this->cert = create_empty();
                        }
-                       this->cert = load(va_arg(args, chunk_t));
+                       handled = FALSE;
+                       break;
+       }
+       if (handled)
+       {
+               va_end(args);
+               return;
+       }
+       
+       switch (part)
+       {
+               case BUILD_PUBLIC_KEY:
+               {
+                       public_key_t *key = va_arg(args, public_key_t*);
+                       this->cert->public_key = key->get_ref(key);
                        break;
                }
-               case BUILD_X509_FLAG:
+               case BUILD_SUBJECT:
                {
-                       this->flags = va_arg(args, x509_flag_t);
+                       identification_t *id = va_arg(args, identification_t*);
+                       this->cert->subject = id->clone(id);
+                       break;
+               }
+               case BUILD_SUBJECT_ALTNAME:
+               {
+                       identification_t *id = va_arg(args, identification_t*);
+                       this->cert->subjectAltNames->insert_last(
+                                                                       this->cert->subjectAltNames, id->clone(id));
+                       break;
+               }
+               case BUILD_NOT_BEFORE_TIME:
+                       this->cert->notBefore = va_arg(args, time_t);
+                       break;
+               case BUILD_NOT_AFTER_TIME:
+                       this->cert->notAfter = va_arg(args, time_t);
+                       break;
+               case BUILD_SERIAL:
+               {
+                       chunk_t serial = va_arg(args, chunk_t);
+                       this->cert->serialNumber = chunk_clone(serial);
                        break;
                }
+               case BUILD_DIGEST_ALG:
+                       this->digest_alg = va_arg(args, int);
+                       break;
                default:
-                       DBG1("ignoring unsupported build part %N", builder_part_names, part);
+                       /* abort if unsupported option */
+                       if (this->cert)
+                       {
+                               destroy(this->cert);
+                       }
+                       builder_cancel(&this->public);
                        break;
        }
        va_end(args);
@@ -1289,6 +1465,9 @@ builder_t *x509_cert_builder(certificate_type_t type)
        
        this->cert = NULL;
        this->flags = 0;
+       this->sign_cert = NULL;
+       this->sign_key = NULL;
+       this->digest_alg = HASH_SHA1;
        this->public.add = (void(*)(builder_t *this, builder_part_t part, ...))add;
        this->public.build = (void*(*)(builder_t *this))build;