Add pkcs12 plugin which adds support for decoding PKCS#12 containers
authorTobias Brunner <tobias@strongswan.org>
Fri, 12 Apr 2013 09:59:01 +0000 (11:59 +0200)
committerTobias Brunner <tobias@strongswan.org>
Wed, 8 May 2013 13:02:39 +0000 (15:02 +0200)
configure.in
src/libstrongswan/Makefile.am
src/libstrongswan/asn1/oid.txt
src/libstrongswan/credentials/containers/container.c
src/libstrongswan/credentials/containers/container.h
src/libstrongswan/credentials/containers/pkcs12.h
src/libstrongswan/plugins/pkcs12/Makefile.am [new file with mode: 0644]
src/libstrongswan/plugins/pkcs12/pkcs12_decode.c [new file with mode: 0644]
src/libstrongswan/plugins/pkcs12/pkcs12_decode.h [new file with mode: 0644]
src/libstrongswan/plugins/pkcs12/pkcs12_plugin.c [new file with mode: 0644]
src/libstrongswan/plugins/pkcs12/pkcs12_plugin.h [new file with mode: 0644]

index c51146a..c858a29 100644 (file)
@@ -129,6 +129,7 @@ ARG_DISBL_SET([pubkey],         [disable RAW public key support plugin.])
 ARG_DISBL_SET([pkcs1],          [disable PKCS1 key decoding plugin.])
 ARG_DISBL_SET([pkcs7],          [disable PKCS7 container support plugin.])
 ARG_DISBL_SET([pkcs8],          [disable PKCS8 private key decoding plugin.])
+ARG_DISBL_SET([pkcs12],         [disable PKCS12 container support plugin.])
 ARG_DISBL_SET([pgp],            [disable PGP key decoding plugin.])
 ARG_DISBL_SET([dnskey],         [disable DNS RR key decoding plugin.])
 ARG_DISBL_SET([sshkey],         [disable SSH key decoding plugin.])
@@ -966,8 +967,9 @@ ADD_PLUGIN([revocation],           [s charon nm cmd])
 ADD_PLUGIN([constraints],          [s charon nm cmd])
 ADD_PLUGIN([pubkey],               [s charon cmd])
 ADD_PLUGIN([pkcs1],                [s charon openac scepclient pki scripts manager medsrv attest nm cmd])
-ADD_PLUGIN([pkcs7],                [s scepclient pki])
+ADD_PLUGIN([pkcs7],                [s scepclient pki scripts])
 ADD_PLUGIN([pkcs8],                [s charon openac scepclient pki scripts manager medsrv attest nm cmd])
+ADD_PLUGIN([pkcs12],               [s charon scepclient pki scripts])
 ADD_PLUGIN([pgp],                  [s charon])
 ADD_PLUGIN([dnskey],               [s charon])
 ADD_PLUGIN([sshkey],               [s charon nm cmd])
@@ -1100,6 +1102,7 @@ AM_CONDITIONAL(USE_PUBKEY, test x$pubkey = xtrue)
 AM_CONDITIONAL(USE_PKCS1, test x$pkcs1 = xtrue)
 AM_CONDITIONAL(USE_PKCS7, test x$pkcs7 = xtrue)
 AM_CONDITIONAL(USE_PKCS8, test x$pkcs8 = xtrue)
+AM_CONDITIONAL(USE_PKCS12, test x$pkcs12 = xtrue)
 AM_CONDITIONAL(USE_PGP, test x$pgp = xtrue)
 AM_CONDITIONAL(USE_DNSKEY, test x$dnskey = xtrue)
 AM_CONDITIONAL(USE_SSHKEY, test x$sshkey = xtrue)
@@ -1296,6 +1299,7 @@ AC_CONFIG_FILES([
        src/libstrongswan/plugins/pkcs1/Makefile
        src/libstrongswan/plugins/pkcs7/Makefile
        src/libstrongswan/plugins/pkcs8/Makefile
+       src/libstrongswan/plugins/pkcs12/Makefile
        src/libstrongswan/plugins/pgp/Makefile
        src/libstrongswan/plugins/dnskey/Makefile
        src/libstrongswan/plugins/sshkey/Makefile
index db1f8cf..2465fb0 100644 (file)
@@ -309,6 +309,13 @@ if MONOLITHIC
 endif
 endif
 
+if USE_PKCS12
+  SUBDIRS += plugins/pkcs12
+if MONOLITHIC
+  libstrongswan_la_LIBADD += plugins/pkcs12/libstrongswan-pkcs12.la
+endif
+endif
+
 if USE_PGP
   SUBDIRS += plugins/pgp
 if MONOLITHIC
index 8cbd07e..6030aa1 100644 (file)
                 0x08         "unstructuredAddress"             OID_UNSTRUCTURED_ADDRESS
                 0x0E         "extensionRequest"                        OID_EXTENSION_REQUEST
                 0x0F         "S/MIME Capabilities"
+                0x16         "certTypes"
+                  0x01       "X.509"                                   OID_X509_CERTIFICATE
+                  0x02       "SDSI"
               0x0c           "PKCS-12"
                 0x01         "pbeIds"
                   0x01       "pbeWithSHAAnd128BitRC4"  OID_PBE_SHA1_RC4_128
                   0x04       "pbeWithSHAAnd2-KeyTripleDES-CBC" OID_PBE_SHA1_3DES_2KEY_CBC
                   0x05       "pbeWithSHAAnd128BitRC2-CBC"              OID_PBE_SHA1_RC2_CBC_128
                   0x06       "pbeWithSHAAnd40BitRC2-CBC"               OID_PBE_SHA1_RC2_CBC_40
+                0x0a         "PKCS-12v1"
+                  0x01       "bagIds"
+                    0x01     "keyBag"                                  OID_P12_KEY_BAG
+                    0x02     "pkcs8ShroudedKeyBag"             OID_P12_PKCS8_KEY_BAG
+                    0x03     "certBag"                                 OID_P12_CERT_BAG
+                    0x04     "crlBag"                                  OID_P12_CRL_BAG
+                    0x05     "secretBag"
+                    0x06     "safeContentsBag"
             0x02             "digestAlgorithm"
               0x02           "md2"                                             OID_MD2
               0x05           "md5"                                             OID_MD5
index 9f01f55..7456d43 100644 (file)
 
 #include "container.h"
 
-ENUM(container_type_names, CONTAINER_PKCS7, CONTAINER_PKCS7_ENCRYPTED_DATA,
+ENUM(container_type_names, CONTAINER_PKCS7, CONTAINER_PKCS12,
        "PKCS7",
        "PKCS7_DATA",
        "PKCS7_SIGNED_DATA",
        "PKCS7_ENVELOPED_DATA",
        "PKCS7_ENCRYPTED_DATA",
+       "PKCS12",
 );
index ff1d1e1..ee32988 100644 (file)
@@ -44,6 +44,8 @@ enum container_type_t {
        CONTAINER_PKCS7_ENVELOPED_DATA,
        /** PKCS#7/CMS "encrypted-data" */
        CONTAINER_PKCS7_ENCRYPTED_DATA,
+       /** A PKCS#12 container */
+       CONTAINER_PKCS12,
 };
 
 /**
index a6c9746..f22ef04 100644 (file)
 #ifndef PKCS12_H_
 #define PKCS12_H_
 
+#include <credentials/containers/container.h>
 #include <crypto/hashers/hasher.h>
 
 typedef enum pkcs12_key_type_t pkcs12_key_type_t;
+typedef struct pkcs12_t pkcs12_t;
 
 /**
  * The types of password based keys used by PKCS#12.
@@ -35,6 +37,31 @@ enum pkcs12_key_type_t {
 };
 
 /**
+ * PKCS#12/PFX container type.
+ */
+struct pkcs12_t {
+
+       /**
+        * Implements container_t.
+        */
+       container_t container;
+
+       /**
+        * Create an enumerator over extracted certificates.
+        *
+        * @return                      enumerator over certificate_t
+        */
+       enumerator_t* (*create_cert_enumerator)(pkcs12_t *this);
+
+       /**
+        * Create an enumerator over extracted private keys.
+        *
+        * @return                      enumerator over private_key_t
+        */
+       enumerator_t* (*create_key_enumerator)(pkcs12_t *this);
+};
+
+/**
  * Derive the keys used in PKCS#12 for password integrity/privacy mode.
  *
  * @param hash                 hash algorithm to use for key derivation
diff --git a/src/libstrongswan/plugins/pkcs12/Makefile.am b/src/libstrongswan/plugins/pkcs12/Makefile.am
new file mode 100644 (file)
index 0000000..1f703bd
--- /dev/null
@@ -0,0 +1,16 @@
+
+INCLUDES = -I$(top_srcdir)/src/libstrongswan
+
+AM_CFLAGS = -rdynamic
+
+if MONOLITHIC
+noinst_LTLIBRARIES = libstrongswan-pkcs12.la
+else
+plugin_LTLIBRARIES = libstrongswan-pkcs12.la
+endif
+
+libstrongswan_pkcs12_la_SOURCES = \
+       pkcs12_plugin.h pkcs12_plugin.c \
+       pkcs12_decode.h pkcs12_decode.c
+
+libstrongswan_pkcs12_la_LDFLAGS = -module -avoid-version
diff --git a/src/libstrongswan/plugins/pkcs12/pkcs12_decode.c b/src/libstrongswan/plugins/pkcs12/pkcs12_decode.c
new file mode 100644 (file)
index 0000000..379f247
--- /dev/null
@@ -0,0 +1,581 @@
+/*
+ * Copyright (C) 2013 Tobias Brunner
+ * Hochschule fuer Technik Rapperswil
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.  See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * 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.
+ */
+
+#include "pkcs12_decode.h"
+
+#include <utils/debug.h>
+#include <asn1/oid.h>
+#include <asn1/asn1.h>
+#include <asn1/asn1_parser.h>
+#include <credentials/sets/mem_cred.h>
+
+typedef struct private_pkcs12_t private_pkcs12_t;
+
+/**
+ * Private data of a pkcs12_t object
+ */
+struct private_pkcs12_t {
+
+       /**
+        * Public interface
+        */
+       pkcs12_t public;
+
+       /**
+        * Contained credentials
+        */
+       mem_cred_t *creds;
+};
+
+METHOD(container_t, get_type, container_type_t,
+       private_pkcs12_t *this)
+{
+       return CONTAINER_PKCS12;
+}
+
+METHOD(container_t, get_data, bool,
+       private_pkcs12_t *this, chunk_t *data)
+{
+       /* we could return the content of the outer-most PKCS#7 container (authSafe)
+        * don't really see the point though */
+       return FALSE;
+}
+
+METHOD(container_t, get_encoding, bool,
+       private_pkcs12_t *this, chunk_t *encoding)
+{
+       /* similar to get_data() we don't have any use for it at the moment */
+       return FALSE;
+}
+
+METHOD(pkcs12_t, create_cert_enumerator, enumerator_t*,
+       private_pkcs12_t *this)
+{
+       return this->creds->set.create_cert_enumerator(&this->creds->set, CERT_ANY,
+                                                                                                  KEY_ANY, NULL, FALSE);
+}
+
+METHOD(pkcs12_t, create_key_enumerator, enumerator_t*,
+       private_pkcs12_t *this)
+{
+       return this->creds->set.create_private_enumerator(&this->creds->set,
+                                                                                                         KEY_ANY, NULL);
+}
+
+METHOD(container_t, destroy, void,
+       private_pkcs12_t *this)
+{
+       this->creds->destroy(this->creds);
+       free(this);
+}
+
+static private_pkcs12_t *pkcs12_create()
+{
+       private_pkcs12_t *this;
+
+       INIT(this,
+               .public = {
+                       .container = {
+                               .get_type = _get_type,
+                               .create_signature_enumerator = (void*)enumerator_create_empty,
+                               .get_data = _get_data,
+                               .get_encoding = _get_encoding,
+                               .destroy = _destroy,
+                       },
+                       .create_cert_enumerator = _create_cert_enumerator,
+                       .create_key_enumerator = _create_key_enumerator,
+               },
+               .creds = mem_cred_create(),
+       );
+       return this;
+}
+
+/**
+ * ASN.1 definition of an CertBag structure
+ */
+static const asn1Object_t certBagObjects[] = {
+       { 0, "CertBag",                 ASN1_SEQUENCE,          ASN1_BODY                       }, /* 0 */
+       { 1,   "certId",                ASN1_OID,                       ASN1_BODY                       }, /* 1 */
+       { 1,   "certValue",             ASN1_CONTEXT_C_0,       ASN1_BODY                       }, /* 2 */
+       { 0, "exit",                    ASN1_EOC,                       ASN1_EXIT                       }
+};
+#define CERT_BAG_ID    1
+#define CERT_BAG_VALUE         2
+
+/**
+ * Parse a CertBag structure and extract certificate
+ */
+static bool add_certificate(private_pkcs12_t *this, int level0, chunk_t blob)
+{
+       asn1_parser_t *parser;
+       chunk_t object;
+       int objectID;
+       int oid = OID_UNKNOWN;
+       bool success = FALSE;
+
+       parser = asn1_parser_create(certBagObjects, blob);
+       parser->set_top_level(parser, level0);
+
+       while (parser->iterate(parser, &objectID, &object))
+       {
+               switch (objectID)
+               {
+                       case CERT_BAG_ID:
+                               oid = asn1_known_oid(object);
+                               break;
+                       case CERT_BAG_VALUE:
+                       {
+                               if (oid == OID_X509_CERTIFICATE &&
+                                       asn1_parse_simple_object(&object, ASN1_OCTET_STRING,
+                                                               parser->get_level(parser)+1, "x509Certificate"))
+                               {
+                                       certificate_t *cert;
+
+                                       DBG2(DBG_ASN, "-- > parsing certificate from PKCS#12");
+                                       cert = lib->creds->create(lib->creds,
+                                                                                         CRED_CERTIFICATE, CERT_X509,
+                                                                                         BUILD_BLOB_ASN1_DER, object,
+                                                                                         BUILD_END);
+                                       if (cert)
+                                       {
+                                               this->creds->add_cert(this->creds, FALSE, cert);
+                                               DBG2(DBG_ASN, "-- < --");
+                                       }
+                                       else
+                                       {
+                                               DBG2(DBG_ASN, "-- < failed parsing certificate from "
+                                                        "PKCS#12");
+                                       }
+                               }
+                               break;
+                       }
+               }
+       }
+       success = parser->success(parser);
+       parser->destroy(parser);
+       return success;
+}
+
+/**
+ * ASN.1 definition of an AuthenticatedSafe structure
+ */
+static const asn1Object_t safeContentsObjects[] = {
+       { 0, "SafeContents",    ASN1_SEQUENCE,          ASN1_LOOP                       }, /* 0 */
+       { 1,   "SafeBag",               ASN1_SEQUENCE,          ASN1_BODY                       }, /* 1 */
+       { 2,     "bagId",               ASN1_OID,                       ASN1_BODY                       }, /* 2 */
+       { 2,     "bagValue",    ASN1_CONTEXT_C_0,       ASN1_BODY                       }, /* 3 */
+       { 2,     "bagAttr",             ASN1_SET,                       ASN1_OPT|ASN1_RAW       }, /* 4 */
+       { 2,     "end opt",             ASN1_EOC,                       ASN1_END                        }, /* 5 */
+       { 0, "end loop",                ASN1_EOC,                       ASN1_END                        }, /* 6 */
+       { 0, "exit",                    ASN1_EOC,                       ASN1_EXIT                       }
+};
+#define SAFE_BAG_ID    2
+#define SAFE_BAG_VALUE         3
+
+/**
+ * Parse a SafeContents structure and extract credentials
+ */
+static bool parse_safe_contents(private_pkcs12_t *this, int level0,
+                                                               chunk_t blob)
+{
+       asn1_parser_t *parser;
+       chunk_t object;
+       int objectID;
+       int oid = OID_UNKNOWN;
+       bool success = FALSE;
+
+       parser = asn1_parser_create(safeContentsObjects, blob);
+       parser->set_top_level(parser, level0);
+
+       while (parser->iterate(parser, &objectID, &object))
+       {
+               switch (objectID)
+               {
+                       case SAFE_BAG_ID:
+                               oid = asn1_known_oid(object);
+                               break;
+                       case SAFE_BAG_VALUE:
+                       {
+                               switch (oid)
+                               {
+                                       case OID_P12_CERT_BAG:
+                                       {
+                                               add_certificate(this, parser->get_level(parser)+1,
+                                                                               object);
+                                               break;
+                                       }
+                                       case OID_P12_KEY_BAG:
+                                       case OID_P12_PKCS8_KEY_BAG:
+                                       {
+                                               private_key_t *key;
+
+                                               DBG2(DBG_ASN, "-- > parsing private key from PKCS#12");
+                                               key = lib->creds->create(lib->creds, CRED_PRIVATE_KEY,
+                                                                               KEY_ANY, BUILD_BLOB_ASN1_DER, object,
+                                                                               BUILD_END);
+                                               if (key)
+                                               {
+                                                       this->creds->add_key(this->creds, key);
+                                                       DBG2(DBG_ASN, "-- < --");
+                                               }
+                                               else
+                                               {
+                                                       DBG2(DBG_ASN, "-- < failed parsing private key "
+                                                                "from PKCS#12");
+                                               }
+                                       }
+                                       default:
+                                               break;
+                               }
+                               break;
+                       }
+               }
+       }
+       success = parser->success(parser);
+       parser->destroy(parser);
+       return success;
+}
+
+/**
+ * ASN.1 definition of an AuthenticatedSafe structure
+ */
+static const asn1Object_t authenticatedSafeObjects[] = {
+       { 0, "AuthenticatedSafe",       ASN1_SEQUENCE,  ASN1_LOOP       }, /* 0 */
+       { 1,   "ContentInfo",           ASN1_SEQUENCE,  ASN1_OBJ        }, /* 1 */
+       { 0, "end loop",                        ASN1_EOC,               ASN1_END        }, /* 2 */
+       { 0, "exit",                            ASN1_EOC,               ASN1_EXIT       }
+};
+#define AUTHENTICATED_SAFE_DATA        1
+
+/**
+ * Parse an AuthenticatedSafe structure
+ */
+static bool parse_authenticated_safe(private_pkcs12_t *this, chunk_t blob)
+{
+       asn1_parser_t *parser;
+       chunk_t object;
+       int objectID;
+       bool success = FALSE;
+
+       parser = asn1_parser_create(authenticatedSafeObjects, blob);
+
+       while (parser->iterate(parser, &objectID, &object))
+       {
+               switch (objectID)
+               {
+                       case AUTHENTICATED_SAFE_DATA:
+                       {
+                               container_t *container;
+                               chunk_t data;
+
+                               container = lib->creds->create(lib->creds, CRED_CONTAINER,
+                                                                               CONTAINER_PKCS7, BUILD_BLOB_ASN1_DER,
+                                                                               object, BUILD_END);
+                               if (!container)
+                               {
+                                       goto end;
+                               }
+                               switch (container->get_type(container))
+                               {
+                                       case CONTAINER_PKCS7_DATA:
+                                       case CONTAINER_PKCS7_ENCRYPTED_DATA:
+                                       case CONTAINER_PKCS7_ENVELOPED_DATA:
+                                               if (container->get_data(container, &data))
+                                               {
+                                                       break;
+                                               }
+                                               /* fall-through */
+                                       default:
+                                               container->destroy(container);
+                                               goto end;
+                               }
+                               container->destroy(container);
+
+                               if (!parse_safe_contents(this, parser->get_level(parser)+1,
+                                                                                data))
+                               {
+                                       chunk_free(&data);
+                                       goto end;
+                               }
+                               chunk_free(&data);
+                               break;
+                       }
+               }
+       }
+       success = parser->success(parser);
+end:
+       parser->destroy(parser);
+       return success;
+}
+
+/**
+ * Verify the given MAC with available passwords.
+ */
+static bool verify_mac(hash_algorithm_t hash, chunk_t salt,
+                                          u_int64_t iterations, chunk_t data, chunk_t mac)
+{
+       integrity_algorithm_t integ;
+       enumerator_t *enumerator;
+       shared_key_t *shared;
+       signer_t *signer;
+       chunk_t key, calculated;
+       bool success = FALSE;
+
+       integ = hasher_algorithm_to_integrity(hash, mac.len);
+       signer = lib->crypto->create_signer(lib->crypto, integ);
+       if (!signer)
+       {
+               return FALSE;
+       }
+       key = chunk_alloca(signer->get_key_size(signer));
+       calculated = chunk_alloca(signer->get_block_size(signer));
+
+       enumerator = lib->credmgr->create_shared_enumerator(lib->credmgr,
+                                                                               SHARED_PRIVATE_KEY_PASS, NULL, NULL);
+       while (enumerator->enumerate(enumerator, &shared, NULL, NULL))
+       {
+               if (!pkcs12_derive_key(hash, shared->get_key(shared), salt, iterations,
+                                                          PKCS12_KEY_MAC, key))
+               {
+                       break;
+               }
+               if (!signer->set_key(signer, key) ||
+                       !signer->get_signature(signer, data, calculated.ptr))
+               {
+                       break;
+               }
+               if (chunk_equals(mac, calculated))
+               {
+                       success = TRUE;
+                       break;
+               }
+       }
+       enumerator->destroy(enumerator);
+       signer->destroy(signer);
+       return success;
+}
+
+/**
+ * ASN.1 definition of digestInfo
+ */
+static const asn1Object_t digestInfoObjects[] = {
+       { 0, "digestInfo",                      ASN1_SEQUENCE,          ASN1_OBJ        }, /*  0 */
+       { 1,   "digestAlgorithm",       ASN1_EOC,                       ASN1_RAW        }, /*  1 */
+       { 1,   "digest",                        ASN1_OCTET_STRING,      ASN1_BODY       }, /*  2 */
+       { 0, "exit",                            ASN1_EOC,                       ASN1_EXIT       }
+};
+#define DIGEST_INFO_ALGORITHM          1
+#define DIGEST_INFO_DIGEST                     2
+
+/**
+ * Parse a digestInfo structure
+ */
+static bool parse_digest_info(chunk_t blob, int level0, hash_algorithm_t *hash,
+                                                         chunk_t *digest)
+{
+       asn1_parser_t *parser;
+       chunk_t object;
+       int objectID;
+       bool success;
+
+       parser = asn1_parser_create(digestInfoObjects, blob);
+       parser->set_top_level(parser, level0);
+
+       while (parser->iterate(parser, &objectID, &object))
+       {
+               switch (objectID)
+
+               {
+                       case DIGEST_INFO_ALGORITHM:
+                       {
+                               int oid = asn1_parse_algorithmIdentifier(object,
+                                                                        parser->get_level(parser)+1, NULL);
+
+                               *hash = hasher_algorithm_from_oid(oid);
+                               break;
+                       }
+                       case DIGEST_INFO_DIGEST:
+                       {
+                               *digest = object;
+                               break;
+                       }
+                       default:
+                               break;
+               }
+       }
+       success = parser->success(parser);
+       parser->destroy(parser);
+       return success;
+}
+
+/**
+ * ASN.1 definition of a PFX structure
+ */
+static const asn1Object_t PFXObjects[] = {
+       { 0, "PFX",                             ASN1_SEQUENCE,          ASN1_NONE                       }, /* 0 */
+       { 1,   "version",               ASN1_INTEGER,           ASN1_BODY                       }, /* 1 */
+       { 1,   "authSafe",              ASN1_SEQUENCE,          ASN1_OBJ                        }, /* 2 */
+       { 1,   "macData",               ASN1_SEQUENCE,          ASN1_OPT|ASN1_BODY      }, /* 3 */
+       { 2,     "mac",                 ASN1_SEQUENCE,          ASN1_RAW                        }, /* 4 */
+       { 2,     "macSalt",             ASN1_OCTET_STRING,      ASN1_BODY                       }, /* 5 */
+       { 2,     "iterations",  ASN1_INTEGER,           ASN1_DEF|ASN1_BODY      }, /* 6 */
+       { 1,   "end opt",               ASN1_EOC,                       ASN1_END                        }, /* 7 */
+       { 0, "exit",                    ASN1_EOC,                       ASN1_EXIT                       }
+};
+#define PFX_AUTH_SAFE  2
+#define PFX_MAC                        4
+#define PFX_SALT               5
+#define PFX_ITERATIONS 6
+
+/**
+ * Parse an ASN.1 encoded PFX structure
+ */
+static bool parse_PFX(private_pkcs12_t *this, chunk_t blob)
+{
+       asn1_parser_t *parser;
+       int objectID;
+       chunk_t object, auth_safe, digest = chunk_empty, salt = chunk_empty,
+                       data = chunk_empty;
+       hash_algorithm_t hash = HASH_UNKNOWN;
+       container_t *container = NULL;
+       u_int64_t iterations = 0;
+       bool success = FALSE;
+
+       parser = asn1_parser_create(PFXObjects, blob);
+
+       while (parser->iterate(parser, &objectID, &object))
+       {
+               switch (objectID)
+               {
+                       case PFX_AUTH_SAFE:
+                       {
+                               auth_safe = object;
+                               break;
+                       }
+                       case PFX_MAC:
+                       {
+                               if (!parse_digest_info(object, parser->get_level(parser)+1,
+                                                                          &hash, &digest))
+                               {
+                                       goto end_parse;
+                               }
+                               break;
+                       }
+                       case PFX_SALT:
+                       {
+                               salt = object;
+                               break;
+                       }
+                       case PFX_ITERATIONS:
+                       {
+                               iterations = object.len ? asn1_parse_integer_uint64(object) : 1;
+                               break;
+                       }
+               }
+       }
+       success = parser->success(parser);
+
+end_parse:
+       parser->destroy(parser);
+       if (!success)
+       {
+               return FALSE;
+       }
+
+       success = FALSE;
+       DBG2(DBG_ASN, "-- > --");
+       container = lib->creds->create(lib->creds, CRED_CONTAINER, CONTAINER_PKCS7,
+                                                                  BUILD_BLOB_ASN1_DER, auth_safe, BUILD_END);
+       if (container && container->get_data(container, &data))
+       {
+               if (hash != HASH_UNKNOWN)
+               {
+                       if (container->get_type(container) != CONTAINER_PKCS7_DATA)
+                       {
+                               goto end;
+                       }
+                       if (!verify_mac(hash, salt, iterations, data, digest))
+                       {
+                               DBG1(DBG_ASN, "  MAC verification of PKCS#12 container failed");
+                               goto end;
+                       }
+               }
+               else
+               {
+                       enumerator_t *enumerator;
+                       auth_cfg_t *auth;
+
+                       if (container->get_type(container) != CONTAINER_PKCS7_SIGNED_DATA)
+                       {
+                               goto end;
+                       }
+                       enumerator = container->create_signature_enumerator(container);
+                       if (!enumerator->enumerate(enumerator, &auth))
+                       {
+                               DBG1(DBG_ASN, "  signature verification of PKCS#12 container "
+                                        "failed");
+                               enumerator->destroy(enumerator);
+                               goto end;
+                       }
+                       enumerator->destroy(enumerator);
+               }
+               success = parse_authenticated_safe(this, data);
+       }
+end:
+       DBG2(DBG_ASN, "-- < --");
+       DESTROY_IF(container);
+       chunk_free(&data);
+       return success;
+}
+
+/**
+ * See header.
+ */
+pkcs12_t *pkcs12_decode(container_type_t type, va_list args)
+{
+       private_pkcs12_t *this;
+       chunk_t blob = chunk_empty;
+
+       while (TRUE)
+       {
+               switch (va_arg(args, builder_part_t))
+               {
+                       case BUILD_BLOB_ASN1_DER:
+                               blob = va_arg(args, chunk_t);
+                               continue;
+                       case BUILD_END:
+                               break;
+                       default:
+                               return NULL;
+               }
+               break;
+       }
+       if (blob.len)
+       {
+               if (blob.len >= 2 &&
+                       blob.ptr[0] == ASN1_SEQUENCE && blob.ptr[1] == 0x80)
+               {       /* looks like infinite length BER encoding, but we can't handle it.
+                        */
+                       return NULL;
+               }
+               this = pkcs12_create();
+               if (parse_PFX(this, blob))
+               {
+                       return &this->public;
+               }
+               destroy(this);
+       }
+       return NULL;
+}
diff --git a/src/libstrongswan/plugins/pkcs12/pkcs12_decode.h b/src/libstrongswan/plugins/pkcs12/pkcs12_decode.h
new file mode 100644 (file)
index 0000000..e299896
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2013 Tobias Brunner
+ * Hochschule fuer Technik Rapperswil
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.  See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * 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.
+ */
+
+/**
+ * @defgroup pkcs12_decode pkcs12_decode
+ * @{ @ingroup pkcs12
+ */
+
+#ifndef PKCS12_DECODE_H_
+#define PKCS12_DECODE_H_
+
+#include <credentials/builder.h>
+#include <credentials/containers/pkcs12.h>
+
+/**
+ * Load a PKCS#12 container.
+ *
+ * The argument list must contain a single BUILD_BLOB_ASN1_DER argument.
+ *
+ * @param type         type of the container, CONTAINER_PKCS12
+ * @param args         builder_part_t argument list
+ * @return                     container, NULL on failure
+ */
+pkcs12_t *pkcs12_decode(container_type_t type, va_list args);
+
+#endif /** PKCS12_DECODE_H_ @}*/
diff --git a/src/libstrongswan/plugins/pkcs12/pkcs12_plugin.c b/src/libstrongswan/plugins/pkcs12/pkcs12_plugin.c
new file mode 100644 (file)
index 0000000..ae0fb90
--- /dev/null
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2012 Tobias Brunner
+ * Hochschule fuer Technik Rapperswil
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.  See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * 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.
+ */
+
+#include "pkcs12_plugin.h"
+
+#include <library.h>
+
+#include "pkcs12_decode.h"
+
+typedef struct private_pkcs12_plugin_t private_pkcs12_plugin_t;
+
+/**
+ * private data of pkcs12_plugin
+ */
+struct private_pkcs12_plugin_t {
+
+       /**
+        * public functions
+        */
+       pkcs12_plugin_t public;
+};
+
+METHOD(plugin_t, get_name, char*,
+       private_pkcs12_plugin_t *this)
+{
+       return "pkcs12";
+}
+
+METHOD(plugin_t, get_features, int,
+       private_pkcs12_plugin_t *this, plugin_feature_t *features[])
+{
+       static plugin_feature_t f[] = {
+               PLUGIN_REGISTER(CONTAINER_DECODE, pkcs12_decode, FALSE),
+                       PLUGIN_PROVIDE(CONTAINER_DECODE, CONTAINER_PKCS12),
+       };
+       *features = f;
+       return countof(f);
+}
+
+METHOD(plugin_t, destroy, void,
+       private_pkcs12_plugin_t *this)
+{
+       free(this);
+}
+
+/*
+ * see header file
+ */
+plugin_t *pkcs12_plugin_create()
+{
+       private_pkcs12_plugin_t *this;
+
+       INIT(this,
+               .public = {
+                       .plugin = {
+                               .get_name = _get_name,
+                               .get_features = _get_features,
+                               .destroy = _destroy,
+                       },
+               },
+       );
+
+       return &this->public.plugin;
+}
+
diff --git a/src/libstrongswan/plugins/pkcs12/pkcs12_plugin.h b/src/libstrongswan/plugins/pkcs12/pkcs12_plugin.h
new file mode 100644 (file)
index 0000000..3bd7f2d
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2013 Tobias Brunner
+ * Hochschule fuer Technik Rapperswil
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.  See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPSE.  See the GNU General Public License
+ * for more details.
+ */
+
+/**
+ * @defgroup pkcs12 pkcs12
+ * @ingroup plugins
+ *
+ * @defgroup pkcs12_plugin pkcs12_plugin
+ * @{ @ingroup pkcs12
+ */
+
+#ifndef PKCS12_PLUGIN_H_
+#define PKCS12_PLUGIN_H_
+
+#include <plugins/plugin.h>
+
+typedef struct pkcs12_plugin_t pkcs12_plugin_t;
+
+/**
+ * Plugin providing PKCS#12 decoding functions
+ */
+struct pkcs12_plugin_t {
+
+       /**
+        * Implements plugin interface.
+        */
+       plugin_t plugin;
+};
+
+#endif /** PKCS12_PLUGIN_H_ @}*/