]> git.ipfire.org Git - thirdparty/strongswan.git/commitdiff
tpm2-loadpkcs12: Load private key from PKCS#12 to TPM 2.0 tpm-loadpkcs12
authorAndreas Steffen <andreas.steffen@strongswan.org>
Thu, 1 Mar 2018 11:57:39 +0000 (12:57 +0100)
committerAndreas Steffen <andreas.steffen@strongswan.org>
Mon, 9 Apr 2018 09:10:40 +0000 (11:10 +0200)
This command loads a password-protected PKCS#12 container,
extracts the private key, end entity and CA certificates
and stores the private key in non-volatile storage under
either the endorsement or owner hierarchy of a TPM 2.0.

configure.ac
src/Makefile.am
src/libtpmtss/tpm_tss.h
src/libtpmtss/tpm_tss_trousers.c
src/libtpmtss/tpm_tss_tss2.c
src/tpm_loadpkcs12/.gitignore [new file with mode: 0644]
src/tpm_loadpkcs12/Makefile.am [new file with mode: 0644]
src/tpm_loadpkcs12/tpm_loadpkcs12.c [new file with mode: 0644]

index ae04fc87c751956504ae9432d116a6bab79b3a49..698c00ce598e2b467e8117105698864ac63bb0e5 100644 (file)
@@ -1993,6 +1993,7 @@ AC_CONFIG_FILES([
        src/scepclient/Makefile
        src/aikgen/Makefile
        src/tpm_extendpcr/Makefile
+       src/tpm_loadpkcs12/Makefile
        src/pki/Makefile
        src/pki/man/Makefile
        src/pool/Makefile
index e2747c300a07e3998b1c3f9ba897ef79ea403e5b..c873e93eb693e3b87c674e030517862c3b37b1ed 100644 (file)
@@ -146,4 +146,5 @@ endif
 
 if USE_TPM
   SUBDIRS += tpm_extendpcr
+  SUBDIRS += tpm_loadpkcs12
 endif
index bcb7ab949f5e0a216ab33f26dc40c7442575f12c..5211c52295ea2095b6836f368a365fd9376d571c 100644 (file)
@@ -155,6 +155,19 @@ struct tpm_tss_t {
        bool (*get_data)(tpm_tss_t *this, uint32_t hierarchy, uint32_t handle,
                                         chunk_t pin, chunk_t *data);
 
+       /**
+        * Permanently load a private key into TPM NV storage (TPM 2.0 only)
+        *
+        * @param handle                object handle to be assigned to TPM key
+        * @param hierarchy             hierarchy the TPM key object is attached to
+        * @param pin                   PIN code or empty chunk
+        * @param type                  private key type
+        * @param encoding              private key encoding
+        * @return                              TRUE if load succeeded
+        */
+       bool (*load_key)(tpm_tss_t *this, uint32_t hierarchy, uint32_t handle,
+                                        chunk_t pin, key_type_t type, chunk_t encoding);
+
        /**
         * Destroy a tpm_tss_t.
         */
index 6ed57af9d06227f93f165f861329504388b6a9e7..2a62eee99af1e8416cb3e827e81b169c603876a4 100644 (file)
@@ -602,6 +602,13 @@ METHOD(tpm_tss_t, get_data, bool,
        return FALSE;
 }
 
+METHOD(tpm_tss_t, load_key, bool,
+       private_tpm_tss_trousers_t *this, uint32_t hierarchy, uint32_t handle,
+       chunk_t pin, key_type_t type, chunk_t encoding)
+{
+       return FALSE;
+}
+
 METHOD(tpm_tss_t, destroy, void,
        private_tpm_tss_trousers_t *this)
 {
@@ -647,6 +654,7 @@ tpm_tss_t *tpm_tss_trousers_create()
                                .sign = _sign,
                                .get_random = _get_random,
                                .get_data = _get_data,
+                               .load_key = _load_key,
                                .destroy = _destroy,
                        },
                        .load_aik = _load_aik,
index 90a16c103ed86d018cce546e0c1986e594f2e165..ece3f4a0343dd9ce73111c8f4117bb79c170e1f9 100644 (file)
@@ -20,6 +20,7 @@
 
 #include <asn1/asn1.h>
 #include <asn1/oid.h>
+#include <asn1/asn1_parser.h>
 #include <bio/bio_reader.h>
 
 #include <tpm20.h>
@@ -1019,7 +1020,7 @@ METHOD(tpm_tss_t, sign, bool,
 METHOD(tpm_tss_t, get_random, bool,
        private_tpm_tss_tss2_t *this, size_t bytes, uint8_t *buffer)
 {
-       size_t len, random_len= sizeof(TPM2B_DIGEST)-2;
+       size_t len, random_len = sizeof(TPM2B_DIGEST)-2;
        TPM2B_DIGEST random = { { random_len, } };
        uint8_t *pos = buffer;
        uint32_t rval;
@@ -1114,6 +1115,183 @@ METHOD(tpm_tss_t, get_data, bool,
        return TRUE;
 }
 
+/**
+ * ASN.1 definition of a PKCS#1 RSA private key
+ */
+static const asn1Object_t privkeyObjects[] = {
+       { 0, "RSAPrivateKey",       ASN1_SEQUENCE,     ASN1_NONE }, /*  0 */
+       { 1,   "version",           ASN1_INTEGER,      ASN1_BODY }, /*  1 */
+       { 1,   "modulus",           ASN1_INTEGER,      ASN1_BODY }, /*  2 */
+       { 1,   "publicExponent",    ASN1_INTEGER,      ASN1_BODY }, /*  3 */
+       { 1,   "privateExponent",   ASN1_INTEGER,      ASN1_BODY }, /*  4 */
+       { 1,   "prime1",            ASN1_INTEGER,      ASN1_BODY }, /*  5 */
+       { 1,   "prime2",            ASN1_INTEGER,      ASN1_BODY }, /*  6 */
+       { 1,   "exponent1",         ASN1_INTEGER,      ASN1_BODY }, /*  7 */
+       { 1,   "exponent2",         ASN1_INTEGER,      ASN1_BODY }, /*  8 */
+       { 1,   "coefficient",       ASN1_INTEGER,      ASN1_BODY }, /*  9 */
+       { 1,   "otherPrimeInfos",   ASN1_SEQUENCE,     ASN1_OPT |
+                                                      ASN1_LOOP }, /* 10 */
+       { 2,     "otherPrimeInfo",  ASN1_SEQUENCE,     ASN1_NONE }, /* 11 */
+       { 3,       "prime",         ASN1_INTEGER,      ASN1_BODY }, /* 12 */
+       { 3,       "exponent",      ASN1_INTEGER,      ASN1_BODY }, /* 13 */
+       { 3,       "coefficient",   ASN1_INTEGER,      ASN1_BODY }, /* 14 */
+       { 1,   "end opt or loop",   ASN1_EOC,          ASN1_END  }, /* 15 */
+       { 0, "exit",                ASN1_EOC,          ASN1_EXIT }
+};
+
+#define PRIV_KEY_VERSION                1
+#define PRIV_KEY_MODULUS                2
+#define PRIV_KEY_PUB_EXP                3
+#define PRIV_KEY_PRIME1                         5
+
+/**
+ * Build a TPM 2.0 RSA key from an ASN.1 encoded private key blob.
+ */
+static bool build_rsa_key(chunk_t blob, TPMT_SENSITIVE *priv, TPMT_PUBLIC *pub)
+{
+       chunk_t n, e, p;
+       asn1_parser_t *parser;
+       chunk_t object;
+       int objectID ;
+       bool success = FALSE;
+
+       TPM2B_PRIVATE_KEY_RSA *priv_rsa = &priv->sensitive.rsa;
+    TPM2B_PUBLIC_KEY_RSA *pub_rsa = &pub->unique.rsa;
+
+       priv->sensitiveType = TPM_ALG_RSA;
+       pub->type = TPM_ALG_RSA;
+       pub->parameters.rsaDetail.symmetric.algorithm = TPM_ALG_NULL;
+       pub->parameters.rsaDetail.scheme.scheme = TPM_ALG_RSASSA;
+       pub->parameters.rsaDetail.scheme.details.anySig.hashAlg = TPM_ALG_SHA256;
+
+       parser = asn1_parser_create(privkeyObjects, blob);
+       parser->set_flags(parser, FALSE, TRUE);
+
+       while (parser->iterate(parser, &objectID, &object))
+       {
+               switch (objectID)
+               {
+                       case PRIV_KEY_VERSION:
+                               if (object.len > 0 && *object.ptr != 0)
+                               {
+                                       goto end;
+                               }
+                               break;
+                       case PRIV_KEY_MODULUS:
+                               n = object;
+                               if (n.len > 0 && *n.ptr == 0x00)
+                               {
+                                       n = chunk_skip(n, 1);
+                               }
+                               if (n.len > MAX_RSA_KEY_BYTES)
+                               {
+                                       goto end;
+                               }
+                               memcpy(pub_rsa->t.buffer, n.ptr, n.len);
+                               pub_rsa->t.size = n.len;
+                               pub->parameters.rsaDetail.keyBits = 8 * n.len;
+                               break;
+                       case PRIV_KEY_PUB_EXP:
+                               e = object;
+                               /* we only accept the standard public exponent 2'16+1 */
+                               if (chunk_equals(e, chunk_from_str("\x01\x00\x01")))
+                               {
+                                       goto end;
+                               }
+                               break;
+                       case PRIV_KEY_PRIME1:
+                               p = object;
+                               if (p.len > 0 && *p.ptr == 0x00)
+                               {
+                                       p = chunk_skip(p, 1);
+                               }
+                               if (p.len > MAX_RSA_KEY_BYTES / 2)
+                               {
+                                       goto end;
+                               }
+                               memcpy(priv_rsa->t.buffer, p.ptr, p.len);
+                               priv_rsa->t.size = p.len;
+                               break;
+               }
+       }
+       success = parser->success(parser);
+
+end:
+       parser->destroy(parser);
+
+       return success;
+}
+
+/**
+ * Build a TPM 2.0 ECC key from an ASN.1 encoded private key blob.
+ */
+static bool build_ecc_key(chunk_t blob, TPMT_SENSITIVE *priv, TPMT_PUBLIC *pub)
+{
+       priv->sensitiveType = TPM_ALG_RSA;
+       pub->type = TPM_ALG_RSA;
+
+       return TRUE;
+}
+
+METHOD(tpm_tss_t, load_key, bool,
+       private_tpm_tss_tss2_t *this, uint32_t hierarchy, uint32_t handle,
+       chunk_t pin, key_type_t type, chunk_t encoding)
+{
+       bool success = FALSE;
+       uint32_t obj_handle, rval;
+
+       TPM2B_SENSITIVE sensitive = { { sizeof(TPM2B_SENSITIVE)-2, } };
+       TPM2B_PUBLIC public       = { { sizeof(TPM2B_PUBLIC)-2, } };
+       TPMT_SENSITIVE *priv = &sensitive.t.sensitiveArea;
+       TPMT_PUBLIC *pub = &public.t.publicArea;
+
+       chunk_t priv_chunk = { (uint8_t*)priv, (size_t)sensitive.t.size };
+       chunk_t pub_chunk  = { (uint8_t*)pub,  (size_t)public.t.size};
+
+       TPM2B_NAME name = { { sizeof(TPM2B_NAME)-2, } };
+
+       TPMS_AUTH_RESPONSE session_data;
+       TSS2_SYS_RSP_AUTHS sessions_data;
+       TPMS_AUTH_RESPONSE *session_data_array[1];
+
+       session_data_array[0]  = &session_data;
+       sessions_data.rspAuths = &session_data_array[0];
+       sessions_data.rspAuthsCount = 1;
+
+       pub->nameAlg = TPM_ALG_SHA256; /* TODO make nameAlg configurable */
+       pub->objectAttributes.val = 0x00040060;
+
+       switch (type)
+       {
+               case KEY_RSA:
+                       success = build_rsa_key(encoding, priv, pub);
+                       break;
+               case KEY_ECDSA:
+                       success = build_ecc_key(encoding, priv, pub);
+                       break;
+               default:
+                       return FALSE;
+       }
+
+       if (!success)
+       {
+               return FALSE;
+       }
+       DBG1(DBG_PTS, "TPM2B_SENSITIVE: %B", &priv_chunk);
+       DBG1(DBG_PTS, "TPM2B_PUBLIC: %B", &pub_chunk);
+
+       rval = Tss2_Sys_LoadExternal(this->sys_context, 0, &sensitive, &public,
+                                        hierarchy, &obj_handle, &name, &sessions_data);
+       if (rval != TPM_RC_SUCCESS)
+       {
+               DBG1(DBG_PTS,"%s Tss2_Sys_LoadExternal failed: 0x%06x", LABEL, rval);
+               return FALSE;
+       }
+       DBG1(DBG_PTS, "handle = 0x%08x", obj_handle);
+
+       return success;
+}
+
 METHOD(tpm_tss_t, destroy, void,
        private_tpm_tss_tss2_t *this)
 {
@@ -1141,6 +1319,7 @@ tpm_tss_t *tpm_tss_tss2_create()
                        .sign = _sign,
                        .get_random = _get_random,
                        .get_data = _get_data,
+                       .load_key = _load_key,
                        .destroy = _destroy,
                },
        );
diff --git a/src/tpm_loadpkcs12/.gitignore b/src/tpm_loadpkcs12/.gitignore
new file mode 100644 (file)
index 0000000..a834a34
--- /dev/null
@@ -0,0 +1 @@
+tpm_loadpkcs12
diff --git a/src/tpm_loadpkcs12/Makefile.am b/src/tpm_loadpkcs12/Makefile.am
new file mode 100644 (file)
index 0000000..6aee030
--- /dev/null
@@ -0,0 +1,14 @@
+bin_PROGRAMS = tpm_loadpkcs12
+
+tpm_loadpkcs12_SOURCES = tpm_loadpkcs12.c
+
+tpm_loadpkcs12_LDADD = \
+       $(top_builddir)/src/libstrongswan/libstrongswan.la \
+       $(top_builddir)/src/libtpmtss/libtpmtss.la
+
+tpm_loadpkcs12.o :     $(top_builddir)/config.status
+
+AM_CPPFLAGS = \
+       -I$(top_srcdir)/src/libstrongswan \
+       -I$(top_srcdir)/src/libtpmtss \
+       -DIPSEC_CONFDIR=\"${sysconfdir}\"
diff --git a/src/tpm_loadpkcs12/tpm_loadpkcs12.c b/src/tpm_loadpkcs12/tpm_loadpkcs12.c
new file mode 100644 (file)
index 0000000..1f0ad3d
--- /dev/null
@@ -0,0 +1,414 @@
+/*
+ * Copyright (C) 2017 Andreas Steffen
+ * HSR 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.
+ */
+
+
+#define _GNU_SOURCE
+#include <syslog.h>
+#include <getopt.h>
+#include <errno.h>
+#include <unistd.h>
+
+#include <tpm_tss.h>
+
+#include <library.h>
+#include <credentials/containers/pkcs12.h>
+#include <credentials/certificates/x509.h>
+#include <credentials/sets/mem_cred.h>
+#include <credentials/sets/callback_cred.h>
+#include <utils/debug.h>
+
+/* logging */
+static bool log_to_stderr = TRUE;
+static bool log_to_syslog = TRUE;
+static level_t default_loglevel = 1;
+
+/**
+ * Global TPM 2.0 instance
+ */
+static tpm_tss_t *tpm;
+
+/**
+ * Global PKCS#12 object
+ */
+static pkcs12_t *p12;
+
+/**
+ * Callback credential set pki uses
+ */
+static callback_cred_t *cb_set;
+
+/**
+ * Credential set to cache entered secrets
+ */
+static mem_cred_t *cb_creds;
+
+static shared_key_type_t prompted;
+
+/**
+ * Callback function to receive credentials
+ */
+static shared_key_t* cb(void *data, shared_key_type_t type,
+                                               identification_t *me, identification_t *other,
+                                               id_match_t *match_me, id_match_t *match_other)
+{
+       char buf[64], *label, *secret = NULL;
+       shared_key_t *shared;
+
+       if (prompted == type)
+       {
+               return NULL;
+       }
+       switch (type)
+       {
+               case SHARED_PIN:
+                       label = "Smartcard PIN";
+                       break;
+               case SHARED_PRIVATE_KEY_PASS:
+                       label = "Private key passphrase";
+                       break;
+               default:
+                       return NULL;
+       }
+       snprintf(buf, sizeof(buf), "%s: ", label);
+#ifdef HAVE_GETPASS
+       secret = getpass(buf);
+#endif
+       if (secret && strlen(secret))
+       {
+               prompted = type;
+               if (match_me)
+               {
+                       *match_me = ID_MATCH_PERFECT;
+               }
+               if (match_other)
+               {
+                       *match_other = ID_MATCH_NONE;
+               }
+               shared = shared_key_create(type, chunk_clone(chunk_from_str(secret)));
+               /* cache password in case it is required more than once */
+               cb_creds->add_shared(cb_creds, shared, NULL);
+               return shared->get_ref(shared);
+       }
+       return NULL;
+}
+
+/**
+ * Register PIN/Passphrase callback function
+ */
+static void add_callback()
+{
+       cb_set = callback_cred_create_shared(cb, NULL);
+       lib->credmgr->add_set(lib->credmgr, &cb_set->set);
+       cb_creds = mem_cred_create();
+       lib->credmgr->add_set(lib->credmgr, &cb_creds->set);
+}
+
+/**
+ * Unregister PIN/Passphrase callback function
+ */
+static void remove_callback()
+{
+       lib->credmgr->remove_set(lib->credmgr, &cb_creds->set);
+       cb_creds->destroy(cb_creds);
+       lib->credmgr->remove_set(lib->credmgr, &cb_set->set);
+       cb_set->destroy(cb_set);
+}
+
+/**
+ * logging function for tpm_loadpkcs12
+ */
+static void tpm_loadpkcs12_dbg(debug_t group, level_t level, char *fmt, ...)
+{
+       char buffer[8192];
+       char *current = buffer, *next;
+       va_list args;
+
+       if (level <= default_loglevel)
+       {
+               if (log_to_stderr)
+               {
+                       va_start(args, fmt);
+                       vfprintf(stderr, fmt, args);
+                       va_end(args);
+                       fprintf(stderr, "\n");
+               }
+               if (log_to_syslog)
+               {
+                       /* write in memory buffer first */
+                       va_start(args, fmt);
+                       vsnprintf(buffer, sizeof(buffer), fmt, args);
+                       va_end(args);
+
+                       /* do a syslog with every line */
+                       while (current)
+                       {
+                               next = strchr(current, '\n');
+                               if (next)
+                               {
+                                       *(next++) = '\0';
+                               }
+                               syslog(LOG_INFO, "%s\n", current);
+                               current = next;
+                       }
+               }
+       }
+}
+
+/**
+ * Initialize logging to stderr/syslog
+ */
+static void init_log(const char *program)
+{
+       dbg = tpm_loadpkcs12_dbg;
+
+       if (log_to_stderr)
+       {
+               setbuf(stderr, NULL);
+       }
+       if (log_to_syslog)
+       {
+               openlog(program, LOG_CONS | LOG_NDELAY | LOG_PID, LOG_AUTHPRIV);
+       }
+}
+
+/**
+ * @brief exit tpm_loadpkcs12
+ *
+ * @param status 0 = OK, -1 = general discomfort
+ */
+static void exit_tpm_loadpkcs12(err_t message, ...)
+{
+       int status = 0;
+
+       DESTROY_IF(tpm);
+       if (p12)
+       {
+               container_t *container = &p12->container;
+
+               container->destroy(container);
+       }
+
+       /* print any error message to stderr */
+       if (message != NULL && *message != '\0')
+       {
+               va_list args;
+               char m[8192];
+
+               va_start(args, message);
+               vsnprintf(m, sizeof(m), message, args);
+               va_end(args);
+
+               fprintf(stderr, "tpm_loadpkcs12 error: %s\n", m);
+               status = -1;
+       }
+       exit(status);
+}
+
+/**
+ * @brief prints the usage of the program to the stderr output
+ *
+ * If message is set, program is exited with 1 (error)
+ * @param message message in case of an error
+ */
+static void usage(const char *message)
+{
+       fprintf(stderr,
+               "Usage: tpm_loadpkcs12 --in <file> [--debug <level>] [--quiet]\n"
+               "       tpm_loadpkcs12  --help\n"
+               "\n"
+               "Options:\n"
+               " --in (-i)      binary input file with digest to be extended\n"
+               " --help (-h)    show usage and exit\n"
+               "\n"
+               "Debugging output:\n"
+               " --debug (-l)   changes the log level (-1..4, default: 1)\n"
+               " --quiet (-q)   do not write log output to stderr\n"
+               );
+       exit_tpm_loadpkcs12(message);
+}
+
+/**
+ * @brief main of tpm_loadpkcs12 which loads a PKCS#12 container and stores
+ *        the key and certificates in a TPM 2.0
+ *
+ * @param argc number of arguments
+ * @param argv pointer to the argument values
+ */
+int main(int argc, char *argv[])
+{
+       uint32_t hierarchy = 0x40000007;  /* TPM_RH_NULL */
+       uint32_t handle = 0;
+       char *infile = NULL;
+       chunk_t id, encoding, pin = chunk_empty;
+       enumerator_t *enumerator;
+       public_key_t *pubkey;
+       private_key_t *key;
+       key_type_t type;
+       certificate_t *cert;
+       x509_flag_t flags;
+       x509_t *x509;
+       bool found, success;
+
+       atexit(library_deinit);
+       if (!library_init(NULL, "tpm_loadpkcs12"))
+       {
+               exit(SS_RC_LIBSTRONGSWAN_INTEGRITY);
+       }
+       if (lib->integrity &&
+               !lib->integrity->check_file(lib->integrity, "tpm_loadpkcs12", argv[0]))
+       {
+               fprintf(stderr, "integrity check of tpm_loadpkcs12 failed\n");
+               exit(SS_RC_DAEMON_INTEGRITY);
+       }
+
+       for (;;)
+       {
+               static const struct option long_opts[] = {
+                       /* name, has_arg, flag, val */
+                       { "help", no_argument, NULL, 'h' },
+                       { "in", required_argument, NULL, 'i' },
+                       { "pin", required_argument, NULL, 'p' },
+                       { "handle", required_argument, NULL, 'H' },
+                       { "quiet", no_argument, NULL, 'q' },
+                       { "debug", required_argument, NULL, 'l' },
+                       { 0,0,0,0 }
+               };
+
+               /* parse next option */
+               int c = getopt_long(argc, argv, "hi:ql:", long_opts, NULL);
+
+               switch (c)
+               {
+                       case EOF:       /* end of flags */
+                               break;
+
+                       case 'h':       /* --help */
+                               usage(NULL);
+
+                       case 'i':       /* --in <file> */
+                               infile = optarg;
+                               continue;
+
+                       case 'H':
+                               continue;
+
+                       case 'p':
+                               pin = chunk_from_str(optarg);
+                               continue;
+
+                       case 'q':       /* --quiet */
+                               log_to_stderr = FALSE;
+                               continue;
+
+                       case 'l':               /* --debug <level> */
+                               default_loglevel = atoi(optarg);
+                               continue;
+
+                       default:
+                               usage("unknown option");
+               }
+               /* break from loop */
+               break;
+       }
+
+       init_log("tpm_loadpkcs12");
+
+       if (!lib->plugins->load(lib->plugins,
+                       lib->settings->get_str(lib->settings, "tpm_loadpkcs12.load",
+                               "random pem openssl tpm")))
+       {
+               exit_tpm_loadpkcs12("plugin loading failed");
+       }
+
+       /* try to find a TPM */
+       tpm = tpm_tss_probe(TPM_VERSION_2_0);
+       if (!tpm)
+       {
+               exit_tpm_loadpkcs12("no TPM 2.0 found");
+       }
+
+       if (!infile)
+       {
+               exit_tpm_loadpkcs12("mandatory --in argument missing");
+       }
+
+       /* add callback prompting for PKCS#12 password */
+       add_callback();
+       atexit(remove_callback);
+
+       p12 = lib->creds->create(lib->creds, CRED_CONTAINER, CONTAINER_PKCS12,
+                                                         BUILD_FROM_FILE, infile, BUILD_END);
+
+       if (!p12)
+       {
+               exit_tpm_loadpkcs12("reading PKCS#12 file failed");
+       }
+       printf("loaded PKCS#12 file from '%s'\n", infile);
+
+       enumerator = p12->create_cert_enumerator(p12);
+       while (enumerator->enumerate(enumerator, &cert))
+       {
+               x509 = (x509_t*)cert;
+               flags = x509->get_flags(x509);
+               printf("%scertificate:\n", (flags & X509_CA) ? "ca " : "");
+
+               pubkey = cert->get_public_key(cert);
+               if (pubkey->get_fingerprint(pubkey, KEYID_PUBKEY_SHA1, &id))
+               {
+                       printf("  subjectKeyIdentifier:      %#B\n", &id);
+               }
+               if (pubkey->get_fingerprint(pubkey, KEYID_PUBKEY_INFO_SHA1, &id))
+               {
+                       printf("  subjectPublicKeyInfo hash: %#B\n", &id);
+               }
+               pubkey->destroy(pubkey);
+       }
+       enumerator->destroy(enumerator);
+
+       enumerator = p12->create_key_enumerator(p12);
+       found = enumerator->enumerate(enumerator, &key);
+       enumerator->destroy(enumerator);
+
+       if (!found)
+       {
+               exit_tpm_loadpkcs12("no private key found in PKCS#12 container");
+       }
+       type = key->get_type(key);
+
+       /* print some private key information */
+       printf("%N private key:\n", key_type_names, type);
+       if (key->get_fingerprint(key, KEYID_PUBKEY_SHA1, &id))
+       {
+               printf("  subjectKeyIdentifier:      %#B\n", &id);
+       }
+       if (key->get_fingerprint(key, KEYID_PUBKEY_INFO_SHA1, &id))
+       {
+               printf("  subjectPublicKeyInfo hash: %#B\n", &id);
+       }
+       if (!key->get_encoding(key, PRIVKEY_ASN1_DER, &encoding))
+       {
+               exit_tpm_loadpkcs12("private key encoding failed");
+       }
+       printf("%B\n", &encoding);
+
+       /* load private key into TPM */
+       success = tpm->load_key(tpm, hierarchy, handle, pin, type, encoding);
+
+       /* cleanup */
+       chunk_clear(&encoding);
+       exit_tpm_loadpkcs12(success ? NULL : "loading into TPM 2.0 failed");
+
+       return -1; /* should never be reached */
+}