]> git.ipfire.org Git - thirdparty/gnutls.git/commitdiff
gnutls_certificate_set_x509_simple_pkcs12_file() now imports certificate chain if...
authorNikos Mavrogiannopoulos <nmav@gnutls.org>
Sun, 3 Jun 2012 10:15:15 +0000 (12:15 +0200)
committerNikos Mavrogiannopoulos <nmav@gnutls.org>
Sun, 3 Jun 2012 10:15:15 +0000 (12:15 +0200)
gnutls_pkcs12_parse() was renamed to gnutls_pkcs12_simple_parse()

lib/gnutls_x509.c
lib/includes/gnutls/pkcs12.h
lib/nettle/pk.c
lib/x509/pkcs12.c

index 9e73d37aff673b3cd932b5c7e83d59beea6aa1f3..b308d6ba23da762c4cf21f159c793d0bd8c54ff3 100644 (file)
@@ -1832,396 +1832,6 @@ gnutls_certificate_set_x509_crl_file (gnutls_certificate_credentials_t res,
 
 #include <gnutls/pkcs12.h>
 
-/**
- * gnutls_pkcs12_parse:
- * @res: is a #gnutls_certificate_credentials_t structure.
- * @p12: the PKCS#12 blob.
- * @password: optional password used to decrypt PKCS#12 blob, bags and keys.
- * @key: a structure to store the parsed private key.
- * @cert: a structure to store the parsed certificate.
- * @extra_certs_ret: optional pointer to receive an array of additional
- *                   certificates found in the PKCS#12 blob.
- * @extra_certs_ret_len: will be updated with the number of additional
- *                       certs.
- * @crl: a structure to store the parsed CRL.
- *
- * This function parses a PKCS#12 blob in @p12blob and extracts the
- * private key, the corresponding certificate, and any additional
- * certificates and a CRL.
- *
- * The @extra_certs_ret and @extra_certs_ret_len parameters are optional
- * and both may be set to %NULL. If either is non-%NULL, then both must
- * be.
- * 
- * MAC:ed PKCS#12 files are supported.  Encrypted PKCS#12 bags are
- * supported.  Encrypted PKCS#8 private keys are supported.  However,
- * only password based security, and the same password for all
- * operations, are supported.
- *
- * The private keys may be RSA PKCS#1 or DSA private keys encoded in
- * the OpenSSL way.
- *
- * PKCS#12 file may contain many keys and/or certificates, and there
- * is no way to identify which key/certificate pair you want.  You
- * should make sure the PKCS#12 file only contain one key/certificate
- * pair and/or one CRL.
- *
- * It is believed that the limitations of this function is acceptable
- * for most usage, and that any more flexibility would introduce
- * complexity that would make it harder to use this functionality at
- * all.
- *
- * Returns: %GNUTLS_E_SUCCESS on success, or an error code.
- *
- * Since: 2.8.0
- **/
-int
-gnutls_pkcs12_parse (gnutls_certificate_credentials_t res,
-                     gnutls_pkcs12_t p12,
-                     const char *password,
-                     gnutls_x509_privkey_t * key,
-                     gnutls_x509_crt_t * cert,
-                     gnutls_x509_crt_t ** extra_certs_ret,
-                     unsigned int * extra_certs_ret_len,
-                     gnutls_x509_crl_t * crl)
-{
-  gnutls_pkcs12_bag_t bag = NULL;
-  gnutls_x509_crt_t *extra_certs = NULL;
-  unsigned int extra_certs_len = 0;
-  int idx = 0;
-  int ret;
-  size_t cert_id_size = 0;
-  size_t key_id_size = 0;
-  uint8_t cert_id[20];
-  uint8_t key_id[20];
-  int privkey_ok = 0;
-
-  *cert = NULL;
-  *key = NULL;
-  *crl = NULL;
-
-  /* find the first private key */
-  for (;;)
-    {
-      int elements_in_bag;
-      int i;
-
-      ret = gnutls_pkcs12_bag_init (&bag);
-      if (ret < 0)
-        {
-          bag = NULL;
-          gnutls_assert ();
-          goto done;
-        }
-
-      ret = gnutls_pkcs12_get_bag (p12, idx, bag);
-      if (ret == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE)
-        break;
-      if (ret < 0)
-        {
-          gnutls_assert ();
-          goto done;
-        }
-
-      ret = gnutls_pkcs12_bag_get_type (bag, 0);
-      if (ret < 0)
-        {
-          gnutls_assert ();
-          goto done;
-        }
-
-      if (ret == GNUTLS_BAG_ENCRYPTED)
-        {
-          ret = gnutls_pkcs12_bag_decrypt (bag, password);
-          if (ret < 0)
-            {
-              gnutls_assert ();
-              goto done;
-            }
-        }
-
-      elements_in_bag = gnutls_pkcs12_bag_get_count (bag);
-      if (elements_in_bag < 0)
-        {
-          gnutls_assert ();
-          goto done;
-        }
-
-      for (i = 0; i < elements_in_bag; i++)
-        {
-          int type;
-          gnutls_datum_t data;
-
-          type = gnutls_pkcs12_bag_get_type (bag, i);
-          if (type < 0)
-            {
-              gnutls_assert ();
-              goto done;
-            }
-
-          ret = gnutls_pkcs12_bag_get_data (bag, i, &data);
-          if (ret < 0)
-            {
-              gnutls_assert ();
-              goto done;
-            }
-
-          switch (type)
-            {
-            case GNUTLS_BAG_PKCS8_ENCRYPTED_KEY:
-            case GNUTLS_BAG_PKCS8_KEY:
-              if (*key != NULL) /* too simple to continue */
-                {
-                  gnutls_assert ();
-                  break;
-                }
-
-              ret = gnutls_x509_privkey_init (key);
-              if (ret < 0)
-                {
-                  gnutls_assert ();
-                  goto done;
-                }
-
-              ret = gnutls_x509_privkey_import_pkcs8
-                (*key, &data, GNUTLS_X509_FMT_DER, password,
-                 type == GNUTLS_BAG_PKCS8_KEY ? GNUTLS_PKCS_PLAIN : 0);
-              if (ret < 0)
-                {
-                  gnutls_assert ();
-                  gnutls_x509_privkey_deinit (*key);
-                  goto done;
-                }
-
-              key_id_size = sizeof (key_id);
-              ret =
-                gnutls_x509_privkey_get_key_id (*key, 0, key_id,
-                                                &key_id_size);
-              if (ret < 0)
-                {
-                  gnutls_assert ();
-                  gnutls_x509_privkey_deinit (*key);
-                  goto done;
-                }
-
-              privkey_ok = 1;   /* break */
-              break;
-            default:
-              break;
-            }
-        }
-
-      idx++;
-      gnutls_pkcs12_bag_deinit (bag);
-
-      if (privkey_ok != 0)      /* private key was found */
-        break;
-    }
-
-  if (privkey_ok == 0)          /* no private key */
-    {
-      gnutls_assert ();
-      return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE;
-    }
-
-  /* now find the corresponding certificate 
-   */
-  idx = 0;
-  bag = NULL;
-  for (;;)
-    {
-      int elements_in_bag;
-      int i;
-
-      ret = gnutls_pkcs12_bag_init (&bag);
-      if (ret < 0)
-        {
-          bag = NULL;
-          gnutls_assert ();
-          goto done;
-        }
-
-      ret = gnutls_pkcs12_get_bag (p12, idx, bag);
-      if (ret == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE)
-        break;
-      if (ret < 0)
-        {
-          gnutls_assert ();
-          goto done;
-        }
-
-      ret = gnutls_pkcs12_bag_get_type (bag, 0);
-      if (ret < 0)
-        {
-          gnutls_assert ();
-          goto done;
-        }
-
-      if (ret == GNUTLS_BAG_ENCRYPTED)
-        {
-          ret = gnutls_pkcs12_bag_decrypt (bag, password);
-          if (ret < 0)
-            {
-              gnutls_assert ();
-              goto done;
-            }
-        }
-
-      elements_in_bag = gnutls_pkcs12_bag_get_count (bag);
-      if (elements_in_bag < 0)
-        {
-          gnutls_assert ();
-          goto done;
-        }
-
-      for (i = 0; i < elements_in_bag; i++)
-        {
-          int type;
-          gnutls_datum_t data;
-          gnutls_x509_crt_t this_cert;
-
-          type = gnutls_pkcs12_bag_get_type (bag, i);
-          if (type < 0)
-            {
-              gnutls_assert ();
-              goto done;
-            }
-
-          ret = gnutls_pkcs12_bag_get_data (bag, i, &data);
-          if (ret < 0)
-            {
-              gnutls_assert ();
-              goto done;
-            }
-
-          switch (type)
-            {
-            case GNUTLS_BAG_CERTIFICATE:
-              ret = gnutls_x509_crt_init (&this_cert);
-              if (ret < 0)
-                {
-                  gnutls_assert ();
-                  goto done;
-                }
-
-              ret =
-                gnutls_x509_crt_import (this_cert, &data, GNUTLS_X509_FMT_DER);
-              if (ret < 0)
-                {
-                  gnutls_assert ();
-                  gnutls_x509_crt_deinit (this_cert);
-                  goto done;
-                }
-
-              /* check if the key id match */
-              cert_id_size = sizeof (cert_id);
-              ret =
-                gnutls_x509_crt_get_key_id (this_cert, 0, cert_id, &cert_id_size);
-              if (ret < 0)
-                {
-                  gnutls_assert ();
-                  gnutls_x509_crt_deinit (this_cert);
-                  goto done;
-                }
-
-              if (memcmp (cert_id, key_id, cert_id_size) != 0)
-                {               /* they don't match - skip the certificate */
-                  if (extra_certs_ret)
-                    {
-                      extra_certs = gnutls_realloc (extra_certs,
-                                                    sizeof(extra_certs[0]) *
-                                                    ++extra_certs_len);
-                      if (!extra_certs)
-                        {
-                          gnutls_assert ();
-                          ret = GNUTLS_E_MEMORY_ERROR;
-                          goto done;
-                        }
-                      extra_certs[extra_certs_len - 1] = this_cert;
-                      this_cert = NULL;
-                    }
-                  else
-                    {
-                       gnutls_x509_crt_deinit (this_cert);
-                    }
-                  break;
-                }
-              else
-                {
-                   if (*cert != NULL)        /* no need to set it again */
-                     {
-                        gnutls_assert ();
-                        break;
-                     }
-                   *cert = this_cert;
-                   this_cert = NULL;
-                }
-              break;
-
-            case GNUTLS_BAG_CRL:
-              if (*crl != NULL)
-                {
-                  gnutls_assert ();
-                  break;
-                }
-
-              ret = gnutls_x509_crl_init (crl);
-              if (ret < 0)
-                {
-                  gnutls_assert ();
-                  goto done;
-                }
-
-              ret = gnutls_x509_crl_import (*crl, &data, GNUTLS_X509_FMT_DER);
-              if (ret < 0)
-                {
-                  gnutls_assert ();
-                  gnutls_x509_crl_deinit (*crl);
-                  goto done;
-                }
-              break;
-
-            case GNUTLS_BAG_ENCRYPTED:
-              /* XXX Bother to recurse one level down?  Unlikely to
-                 use the same password anyway. */
-            case GNUTLS_BAG_EMPTY:
-            default:
-              break;
-            }
-        }
-
-      idx++;
-      gnutls_pkcs12_bag_deinit (bag);
-    }
-
-  ret = 0;
-
-done:
-  if (bag)
-    gnutls_pkcs12_bag_deinit (bag);
-
-  if (ret)
-    {
-      if (*key)
-        gnutls_x509_privkey_deinit(*key);
-      if (*cert)
-        gnutls_x509_crt_deinit(*cert);
-      if (extra_certs_len)
-        {
-          int i;
-          for (i = 0; i < extra_certs_len; i++)
-            gnutls_x509_crt_deinit(extra_certs[i]);
-          gnutls_free(extra_certs);
-        }
-    }
-  else if (extra_certs_ret)
-    {
-      *extra_certs_ret = extra_certs;
-      *extra_certs_ret_len = extra_certs_len;
-    }
-
-  return ret;
-}
 
 /**
  * gnutls_certificate_set_x509_simple_pkcs12_file:
@@ -2277,6 +1887,8 @@ int
   return ret;
 }
 
+#define MAX_CERT_LIST 16
+
 /**
  * gnutls_certificate_set_x509_simple_pkcs12_mem:
  * @res: is a #gnutls_certificate_credentials_t structure.
@@ -2316,7 +1928,9 @@ int
   gnutls_pkcs12_t p12;
   gnutls_x509_privkey_t key = NULL;
   gnutls_x509_crt_t cert = NULL;
+  gnutls_x509_crt_t *extra_certs = NULL;
   gnutls_x509_crl_t crl = NULL;
+  unsigned int extra_certs_size = 0, i;
   int ret;
 
   ret = gnutls_pkcs12_init (&p12);
@@ -2345,7 +1959,8 @@ int
         }
     }
 
-  ret = gnutls_pkcs12_parse (res, p12, password, &key, &cert, NULL, NULL, &crl);
+  ret = gnutls_pkcs12_simple_parse (p12, password, &key, &cert, 
+                             &extra_certs, &extra_certs_size, &crl);
   gnutls_pkcs12_deinit (p12);
   if (ret < 0)
     {
@@ -2355,7 +1970,40 @@ int
 
   if (key && cert)
     {
-      ret = gnutls_certificate_set_x509_key (res, &cert, 1, key);
+      gnutls_x509_crt_t chain[MAX_CERT_LIST];
+      unsigned int chain_size = 1, j;
+      unsigned int done = 0;
+      
+      j = 0;
+      chain[j] = cert;
+
+      if (extra_certs_size > 0 && extra_certs_size < MAX_CERT_LIST-1)
+        {
+          do 
+            {
+              for (i=0;i<extra_certs_size;i++)
+                {
+                  if (gnutls_x509_crt_check_issuer(chain[j], extra_certs[i]) != 0 &&
+                      gnutls_x509_crt_check_issuer(extra_certs[i], chain[j]) == 0)
+                    {
+                      if (gnutls_x509_crt_check_issuer(extra_certs[i], extra_certs[i]) != 0)
+                        { /* we found a self-signed one. We are done. */
+                          done = 1;
+                          break;
+                        }
+                      chain[++j] = extra_certs[i];
+                      chain_size++;
+                      break;
+                    }
+
+                  if (i==extra_certs_size - 1) 
+                    done = 1;
+                }
+            }
+          while(done == 0);
+        }
+
+      ret = gnutls_certificate_set_x509_key (res, chain, chain_size, key);
       if (ret < 0)
         {
           gnutls_assert ();
@@ -2376,6 +2024,12 @@ int
   ret = 0;
 
 done:
+  if (extra_certs)
+    {
+      for (i=0;i<extra_certs_size;i++)
+        gnutls_x509_crt_deinit (extra_certs[i]);
+      gnutls_free(extra_certs);
+    }
   if (cert)
     gnutls_x509_crt_deinit (cert);
   if (key)
index 5fa007ffca11bfc8598dbf4bdf2291b8faa73ecd..dde5b842752e902c7e2f5b9f893a2a3fae90b2c1 100644 (file)
@@ -57,7 +57,7 @@ extern "C"
   int gnutls_pkcs12_bag_decrypt (gnutls_pkcs12_bag_t bag, const char *pass);
   int gnutls_pkcs12_bag_encrypt (gnutls_pkcs12_bag_t bag, const char *pass,
                                  unsigned int flags);
-  int gnutls_pkcs12_parse (gnutls_certificate_credentials_t res,
+  int gnutls_pkcs12_simple_parse (
                            gnutls_pkcs12_t p12,
                            const char *password,
                            gnutls_x509_privkey_t * key,
index ea46f4c880f05670ec109344f0a8fe35767afda2..41a0fb1af1fafc629f34f3fba3757b3b06c896f9 100644 (file)
@@ -264,7 +264,6 @@ _wrap_nettle_pk_decrypt (gnutls_pk_algorithm_t algo,
         unsigned length;
         bigint_t c;
 
-        memset(&priv, 0, sizeof(priv));
         _rsa_params_to_privkey (pk_params, &priv);
         _rsa_params_to_pubkey (pk_params, &pub);
 
@@ -409,15 +408,16 @@ _wrap_nettle_pk_sign (gnutls_pk_algorithm_t algo,
     case GNUTLS_PK_RSA:
       {
         struct rsa_private_key priv;
+        struct rsa_public_key pub;
         mpz_t s;
 
-        memset(&priv, 0, sizeof(priv));
         _rsa_params_to_privkey (pk_params, &priv);
+        _rsa_params_to_pubkey (pk_params, &pub);
         
         mpz_init(s);
 
-/* XXX update when _tr is available */
-        ret = rsa_pkcs1_sign(&priv, vdata->size, vdata->data, s);
+        ret = rsa_pkcs1_sign_tr(&pub, &priv, NULL, rnd_func,
+                                vdata->size, vdata->data, s);
         if (ret == 0)
           {
             gnutls_assert();
index f26fd4fa3516ac235ef886fc707db45d08e03668..40b3a8a2d903553a8700b073e30e8f1d09ccd29a 100644 (file)
@@ -1328,3 +1328,391 @@ cleanup:
 
 }
 
+/**
+ * gnutls_pkcs12_simple_parse:
+ * @p12: the PKCS#12 blob.
+ * @password: optional password used to decrypt PKCS#12 blob, bags and keys.
+ * @key: a structure to store the parsed private key.
+ * @cert: a structure to store the parsed certificate.
+ * @extra_certs_ret: optional pointer to receive an array of additional
+ *                   certificates found in the PKCS#12 blob.
+ * @extra_certs_ret_len: will be updated with the number of additional
+ *                       certs.
+ * @crl: a structure to store the parsed CRL.
+ *
+ * This function parses a PKCS#12 blob in @p12blob and extracts the
+ * private key, the corresponding certificate, and any additional
+ * certificates and a CRL.
+ *
+ * The @extra_certs_ret and @extra_certs_ret_len parameters are optional
+ * and both may be set to %NULL. If either is non-%NULL, then both must
+ * be.
+ * 
+ * MAC:ed PKCS#12 files are supported.  Encrypted PKCS#12 bags are
+ * supported.  Encrypted PKCS#8 private keys are supported.  However,
+ * only password based security, and the same password for all
+ * operations, are supported.
+ *
+ * The private keys may be RSA PKCS#1 or DSA private keys encoded in
+ * the OpenSSL way.
+ *
+ * PKCS#12 file may contain many keys and/or certificates, and there
+ * is no way to identify which key/certificate pair you want.  You
+ * should make sure the PKCS#12 file only contain one key/certificate
+ * pair and/or one CRL.
+ *
+ * It is believed that the limitations of this function is acceptable
+ * for most usage, and that any more flexibility would introduce
+ * complexity that would make it harder to use this functionality at
+ * all.
+ *
+ * Returns: %GNUTLS_E_SUCCESS on success, or an error code.
+ *
+ * Since: 3.1
+ **/
+int
+gnutls_pkcs12_simple_parse (gnutls_pkcs12_t p12,
+                     const char *password,
+                     gnutls_x509_privkey_t * key,
+                     gnutls_x509_crt_t * cert,
+                     gnutls_x509_crt_t ** extra_certs_ret,
+                     unsigned int * extra_certs_ret_len,
+                     gnutls_x509_crl_t * crl)
+{
+  gnutls_pkcs12_bag_t bag = NULL;
+  gnutls_x509_crt_t *extra_certs = NULL;
+  unsigned int extra_certs_len = 0;
+  int idx = 0;
+  int ret;
+  size_t cert_id_size = 0;
+  size_t key_id_size = 0;
+  uint8_t cert_id[20];
+  uint8_t key_id[20];
+  int privkey_ok = 0;
+
+  *cert = NULL;
+  *key = NULL;
+  *crl = NULL;
+
+  /* find the first private key */
+  for (;;)
+    {
+      int elements_in_bag;
+      int i;
+
+      ret = gnutls_pkcs12_bag_init (&bag);
+      if (ret < 0)
+        {
+          bag = NULL;
+          gnutls_assert ();
+          goto done;
+        }
+
+      ret = gnutls_pkcs12_get_bag (p12, idx, bag);
+      if (ret == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE)
+        break;
+      if (ret < 0)
+        {
+          gnutls_assert ();
+          goto done;
+        }
+
+      ret = gnutls_pkcs12_bag_get_type (bag, 0);
+      if (ret < 0)
+        {
+          gnutls_assert ();
+          goto done;
+        }
+
+      if (ret == GNUTLS_BAG_ENCRYPTED)
+        {
+          ret = gnutls_pkcs12_bag_decrypt (bag, password);
+          if (ret < 0)
+            {
+              gnutls_assert ();
+              goto done;
+            }
+        }
+
+      elements_in_bag = gnutls_pkcs12_bag_get_count (bag);
+      if (elements_in_bag < 0)
+        {
+          gnutls_assert ();
+          goto done;
+        }
+
+      for (i = 0; i < elements_in_bag; i++)
+        {
+          int type;
+          gnutls_datum_t data;
+
+          type = gnutls_pkcs12_bag_get_type (bag, i);
+          if (type < 0)
+            {
+              gnutls_assert ();
+              goto done;
+            }
+
+          ret = gnutls_pkcs12_bag_get_data (bag, i, &data);
+          if (ret < 0)
+            {
+              gnutls_assert ();
+              goto done;
+            }
+
+          switch (type)
+            {
+            case GNUTLS_BAG_PKCS8_ENCRYPTED_KEY:
+            case GNUTLS_BAG_PKCS8_KEY:
+              if (*key != NULL) /* too simple to continue */
+                {
+                  gnutls_assert ();
+                  break;
+                }
+
+              ret = gnutls_x509_privkey_init (key);
+              if (ret < 0)
+                {
+                  gnutls_assert ();
+                  goto done;
+                }
+
+              ret = gnutls_x509_privkey_import_pkcs8
+                (*key, &data, GNUTLS_X509_FMT_DER, password,
+                 type == GNUTLS_BAG_PKCS8_KEY ? GNUTLS_PKCS_PLAIN : 0);
+              if (ret < 0)
+                {
+                  gnutls_assert ();
+                  gnutls_x509_privkey_deinit (*key);
+                  goto done;
+                }
+
+              key_id_size = sizeof (key_id);
+              ret =
+                gnutls_x509_privkey_get_key_id (*key, 0, key_id,
+                                                &key_id_size);
+              if (ret < 0)
+                {
+                  gnutls_assert ();
+                  gnutls_x509_privkey_deinit (*key);
+                  goto done;
+                }
+
+              privkey_ok = 1;   /* break */
+              break;
+            default:
+              break;
+            }
+        }
+
+      idx++;
+      gnutls_pkcs12_bag_deinit (bag);
+
+      if (privkey_ok != 0)      /* private key was found */
+        break;
+    }
+
+  if (privkey_ok == 0)          /* no private key */
+    {
+      gnutls_assert ();
+      return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE;
+    }
+
+  /* now find the corresponding certificate 
+   */
+  idx = 0;
+  bag = NULL;
+  for (;;)
+    {
+      int elements_in_bag;
+      int i;
+
+      ret = gnutls_pkcs12_bag_init (&bag);
+      if (ret < 0)
+        {
+          bag = NULL;
+          gnutls_assert ();
+          goto done;
+        }
+
+      ret = gnutls_pkcs12_get_bag (p12, idx, bag);
+      if (ret == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE)
+        break;
+      if (ret < 0)
+        {
+          gnutls_assert ();
+          goto done;
+        }
+
+      ret = gnutls_pkcs12_bag_get_type (bag, 0);
+      if (ret < 0)
+        {
+          gnutls_assert ();
+          goto done;
+        }
+
+      if (ret == GNUTLS_BAG_ENCRYPTED)
+        {
+          ret = gnutls_pkcs12_bag_decrypt (bag, password);
+          if (ret < 0)
+            {
+              gnutls_assert ();
+              goto done;
+            }
+        }
+
+      elements_in_bag = gnutls_pkcs12_bag_get_count (bag);
+      if (elements_in_bag < 0)
+        {
+          gnutls_assert ();
+          goto done;
+        }
+
+      for (i = 0; i < elements_in_bag; i++)
+        {
+          int type;
+          gnutls_datum_t data;
+          gnutls_x509_crt_t this_cert;
+
+          type = gnutls_pkcs12_bag_get_type (bag, i);
+          if (type < 0)
+            {
+              gnutls_assert ();
+              goto done;
+            }
+
+          ret = gnutls_pkcs12_bag_get_data (bag, i, &data);
+          if (ret < 0)
+            {
+              gnutls_assert ();
+              goto done;
+            }
+
+          switch (type)
+            {
+            case GNUTLS_BAG_CERTIFICATE:
+              ret = gnutls_x509_crt_init (&this_cert);
+              if (ret < 0)
+                {
+                  gnutls_assert ();
+                  goto done;
+                }
+
+              ret =
+                gnutls_x509_crt_import (this_cert, &data, GNUTLS_X509_FMT_DER);
+              if (ret < 0)
+                {
+                  gnutls_assert ();
+                  gnutls_x509_crt_deinit (this_cert);
+                  goto done;
+                }
+
+              /* check if the key id match */
+              cert_id_size = sizeof (cert_id);
+              ret =
+                gnutls_x509_crt_get_key_id (this_cert, 0, cert_id, &cert_id_size);
+              if (ret < 0)
+                {
+                  gnutls_assert ();
+                  gnutls_x509_crt_deinit (this_cert);
+                  goto done;
+                }
+
+              if (memcmp (cert_id, key_id, cert_id_size) != 0)
+                {               /* they don't match - skip the certificate */
+                  if (extra_certs_ret)
+                    {
+                      extra_certs = gnutls_realloc (extra_certs,
+                                                    sizeof(extra_certs[0]) *
+                                                    ++extra_certs_len);
+                      if (!extra_certs)
+                        {
+                          gnutls_assert ();
+                          ret = GNUTLS_E_MEMORY_ERROR;
+                          goto done;
+                        }
+                      extra_certs[extra_certs_len - 1] = this_cert;
+                      this_cert = NULL;
+                    }
+                  else
+                    {
+                       gnutls_x509_crt_deinit (this_cert);
+                    }
+                  break;
+                }
+              else
+                {
+                   if (*cert != NULL)        /* no need to set it again */
+                     {
+                        gnutls_assert ();
+                        break;
+                     }
+                   *cert = this_cert;
+                   this_cert = NULL;
+                }
+              break;
+
+            case GNUTLS_BAG_CRL:
+              if (*crl != NULL)
+                {
+                  gnutls_assert ();
+                  break;
+                }
+
+              ret = gnutls_x509_crl_init (crl);
+              if (ret < 0)
+                {
+                  gnutls_assert ();
+                  goto done;
+                }
+
+              ret = gnutls_x509_crl_import (*crl, &data, GNUTLS_X509_FMT_DER);
+              if (ret < 0)
+                {
+                  gnutls_assert ();
+                  gnutls_x509_crl_deinit (*crl);
+                  goto done;
+                }
+              break;
+
+            case GNUTLS_BAG_ENCRYPTED:
+              /* XXX Bother to recurse one level down?  Unlikely to
+                 use the same password anyway. */
+            case GNUTLS_BAG_EMPTY:
+            default:
+              break;
+            }
+        }
+
+      idx++;
+      gnutls_pkcs12_bag_deinit (bag);
+    }
+
+  ret = 0;
+
+done:
+  if (bag)
+    gnutls_pkcs12_bag_deinit (bag);
+
+  if (ret)
+    {
+      if (*key)
+        gnutls_x509_privkey_deinit(*key);
+      if (*cert)
+        gnutls_x509_crt_deinit(*cert);
+      if (extra_certs_len)
+        {
+          unsigned int i;
+          for (i = 0; i < extra_certs_len; i++)
+            gnutls_x509_crt_deinit(extra_certs[i]);
+          gnutls_free(extra_certs);
+        }
+    }
+  else if (extra_certs_ret)
+    {
+      *extra_certs_ret = extra_certs;
+      *extra_certs_ret_len = extra_certs_len;
+    }
+
+  return ret;
+}