]> git.ipfire.org Git - thirdparty/gnutls.git/commitdiff
Some additions to allow decrypting PKCS #5 encrypted data, with PKCS #12 schema OIDs.
authorNikos Mavrogiannopoulos <nmav@gnutls.org>
Sun, 22 Jun 2003 15:41:35 +0000 (15:41 +0000)
committerNikos Mavrogiannopoulos <nmav@gnutls.org>
Sun, 22 Jun 2003 15:41:35 +0000 (15:41 +0000)
lib/Makefile.am
lib/pkix.asn
lib/pkix_asn1_tab.c
lib/x509/Makefile.am
lib/x509/common.h
lib/x509/pkcs12.h
lib/x509/pkcs12_bag.c
lib/x509/pkcs12_encr.c [new file with mode: 0644]
lib/x509/privkey_pkcs8.c

index 23016e86138484566e97aee9307d70d471295728..f261e7fb6756222c4dcc049afb480ab4aebb4a37 100644 (file)
@@ -49,7 +49,8 @@ libgnutls_la_SOURCES = $(COBJECTS)
 libgnutls_la_LIBADD = $(MINITASN1_OBJECTS) x509/dn.lo x509/crl.lo x509/common.lo \
        x509/x509.lo x509/extensions.lo x509/compat.lo x509/verify.lo \
        x509/mpi.lo x509/privkey.lo x509/pkcs7.lo x509/crq.lo x509/xml.lo x509/sign.lo \
-       x509/pkcs5.lo x509/privkey_pkcs8.lo x509/pkcs12.lo x509/pkcs12_bag.lo
+       x509/pkcs5.lo x509/privkey_pkcs8.lo x509/pkcs12.lo x509/pkcs12_bag.lo \
+       x509/pkcs12_encr.lo
 
 libgnutls_la_LDFLAGS = $(LIBASN1_LINK) $(LIBGCRYPT_LIBS) \
        -version-info $(LT_CURRENT):$(LT_REVISION):$(LT_AGE)
index 62d05de9a3b82c2a2904eef26499392ebc626c46..b8b90c51dd16d9c26fa1b88033a949f58ec09b20 100644 (file)
@@ -1094,6 +1094,11 @@ pkcs-12-PFX ::= SEQUENCE {
        macData         pkcs-12-MacData OPTIONAL
 }
 
+pkcs-12-PbeParams ::= SEQUENCE {
+       salt    OCTET STRING,
+       iterations INTEGER
+}
+
 pkcs-12-MacData ::= ANY
 -- we don't use it
 -- SEQUENCE {
index cfe46a72bab23d0482604e5c11a7706da03f325a..2ce535bc943756033d3032b9b467c7478eb21263 100644 (file)
@@ -979,6 +979,9 @@ const ASN1_ARRAY_TYPE pkix_asn1_tab[]={
   {"v3",1,"3"},
   {"authSafe",1073741826,"pkcs-7-ContentInfo"},
   {"macData",16386,"pkcs-12-MacData"},
+  {"pkcs-12-PbeParams",1610612741,0},
+  {"salt",1073741831,0},
+  {"iterations",3,0},
   {"pkcs-12-MacData",1073741837,0},
   {"pkcs-12-AuthenticatedSafe",1610612747,0},
   {0,2,"pkcs-7-ContentInfo"},
index f5730d938d28fe358cb0583ed56e000b9b8b7901..8e3696db04c333a6f6a414801cace971b38c7423 100644 (file)
@@ -9,7 +9,7 @@ noinst_LTLIBRARIES = libx509.la
 COBJECTS = crl.c dn.c common.c x509.c extensions.c \
        rfc2818_hostname.c verify.c mpi.c privkey.c pkcs7.c \
        crq.c xml.c sign.c pkcs5.c privkey_pkcs8.c pkcs12.c \
-       pkcs12_bag.c
+       pkcs12_bag.c pkcs12_encr.c
 
 COMPAT_OBJECTS = compat.c
 
index dc7007bb0d2a516d87d20d90813226bff49b428c..f40480d3778c20f318a2cc09162db8235c4cc10c 100644 (file)
@@ -48,3 +48,6 @@ int _gnutls_x509_export_int( ASN1_TYPE asn1_data,
        int tmp_buf_size, unsigned char* output_data, int* output_data_size);
 
 int _gnutls_x509_read_value( ASN1_TYPE c, const char* root, gnutls_datum *ret, int str);
+
+int _gnutls_x509_decrypt_pkcs7_encrypted_data( const gnutls_datum* data,
+        const char* password, gnutls_datum* dec);
index 256c3535601edcbb55acf8151b536ddf76d3753b..272fbb46081d1865ba4513562f8d3eca68c4935c 100644 (file)
@@ -31,3 +31,7 @@ int gnutls_pkcs12_get_bag(gnutls_pkcs12 pkcs12,
 
 int gnutls_pkcs12_bag_init(gnutls_pkcs12_bag * bag);
 void gnutls_pkcs12_bag_deinit(gnutls_pkcs12_bag bag);
+
+int 
+_pkcs12_string_to_key (int id, const char *salt, int salt_size, int iter, const char *pw,
+               int req_keylen, unsigned char *keybuf);
index 9db5ee5a255f334604932c63946ca4962b440087..2c3f397ac05d4cf99119924db76b293f0202d884 100644 (file)
@@ -95,5 +95,35 @@ int gnutls_pkcs12_bag_get_data(gnutls_pkcs12_bag bag, gnutls_datum* data)
        return 0;
 }
 
+/**
+  * gnutls_pkcs12_bag_decrypt - This function will decrypt an encrypted bag
+  * @bag: The bag
+  * @pass: The password used for encryption
+  *
+  * This function will return 0 on success.
+  *
+  **/
+int gnutls_pkcs12_bag_decrypt(gnutls_pkcs12_bag bag, const char* pass)
+{
+int ret;
+gnutls_datum dec;
+       
+       ret = _gnutls_x509_decrypt_pkcs7_encrypted_data( 
+               &bag->data, pass, &dec);
+
+        if (ret < 0) {
+               gnutls_assert();
+               return ret;
+        }
+        
+        /* decryption succeeded */
+        
+        _gnutls_free_datum( &bag->data);
+        
+        bag->data = dec;
+
+       return 0;
+}
+
 
 #endif /* ENABLE_PKI */
diff --git a/lib/x509/pkcs12_encr.c b/lib/x509/pkcs12_encr.c
new file mode 100644 (file)
index 0000000..ec91797
--- /dev/null
@@ -0,0 +1,92 @@
+/* Taken from minip12
+ */
+
+#include <gcrypt.h>
+#include <gnutls_errors.h>
+
+int 
+_pkcs12_string_to_key (int id, const char *salt, int salt_size, int iter, const char *pw,
+               int req_keylen, unsigned char *keybuf)
+{
+  int rc, i, j;
+  GcryMDHd md;
+  GcryMPI num_b1 = NULL;
+  int pwlen;
+  unsigned char hash[20], buf_b[64], buf_i[128], *p;
+  size_t cur_keylen;
+  size_t n;
+
+  cur_keylen = 0;
+  pwlen = strlen (pw);
+  if (pwlen > 63/2 || salt_size > 8)
+    {
+      return GNUTLS_E_INVALID_REQUEST;
+    }
+
+  /* Store salt and password in BUF_I */
+  p = buf_i;
+  for(i=0; i < 64; i++)
+    *p++ = salt [i%8];
+  for(i=j=0; i < 64; i += 2)
+    {
+      *p++ = 0;
+      *p++ = pw[j];
+      if (++j > pwlen) /* Note, that we include the trailing zero */
+        j = 0;
+    }
+
+  for (;;)
+    {
+      md = gcry_md_open (GCRY_MD_SHA1, 0);
+      if (!md)
+        {
+          return GNUTLS_E_DECRYPTION_FAILED;
+        }
+      for(i=0; i < 64; i++)
+        gcry_md_putc (md, id);
+      gcry_md_write (md, buf_i, 128);
+      memcpy (hash, gcry_md_read (md, 0), 20);
+      gcry_md_close (md);
+      for (i=1; i < iter; i++)
+        gcry_md_hash_buffer (GCRY_MD_SHA1, hash, hash, 20);
+
+      for (i=0; i < 20 && cur_keylen < req_keylen; i++)
+        keybuf[cur_keylen++] = hash[i];
+      if (cur_keylen == req_keylen)
+        {
+          gcry_mpi_release (num_b1);
+          return 0; /* ready */
+        }
+      
+      /* need more bytes. */
+      for(i=0; i < 64; i++)
+        buf_b[i] = hash[i % 20];
+      n = 64;
+      rc = gcry_mpi_scan (&num_b1, GCRYMPI_FMT_USG, buf_b, &n);
+      if (rc)
+        {
+          return GNUTLS_E_DECRYPTION_FAILED;
+        }
+      gcry_mpi_add_ui (num_b1, num_b1, 1);
+      for (i=0; i < 128; i += 64)
+        {
+          GcryMPI num_ij;
+
+          n = 64;
+          rc = gcry_mpi_scan (&num_ij, GCRYMPI_FMT_USG, buf_i + i, &n);
+          if (rc)
+            {
+              return GNUTLS_E_DECRYPTION_FAILED;
+            }
+          gcry_mpi_add (num_ij, num_ij, num_b1);
+          gcry_mpi_clear_highbit (num_ij, 64*8);
+          n = 64;
+          rc = gcry_mpi_print (GCRYMPI_FMT_USG, buf_i + i, &n, num_ij);
+          if (rc)
+            {
+              return GNUTLS_E_DECRYPTION_FAILED;
+            }
+          gcry_mpi_release (num_ij);
+        }
+    }
+}
index 9fc1809c7b47520ebaa96c969be468754f24bbcf..218bdba32c37e6611c91972d30139971e52c2a66 100644 (file)
@@ -33,6 +33,7 @@
 #include <x509.h>
 #include <dn.h>
 #include <pkcs5.h>
+#include <pkcs12.h>
 #include <privkey.h>
 #include <extensions.h>
 #include <mpi.h>
 #include <gnutls_random.h>
 #include <gnutls_num.h>
 
+typedef enum schema_id {
+       PBES2,                  /* the stuff in PKCS #5 */
+       PKCS12_3DES_SHA1 /* the fucking stuff in PKCS #12 */
+} schema_id;
+
 #define PBES2_OID "1.2.840.113549.1.5.13"
 #define PBKDF2_OID "1.2.840.113549.1.5.12"
 #define DES_EDE3_CBC_OID "1.2.840.113549.3.7"
@@ -55,37 +61,66 @@ struct pbkdf2_params {
 };
 
 struct pbe_enc_params {
+       gnutls_cipher_algorithm cipher;
        opaque iv[8];
        int iv_size;
 };
 
-static int generate_key( const char* password, unsigned int flags, struct pbkdf2_params* kdf_params, 
-       struct pbe_enc_params* enc_params, gnutls_datum * key);
-static int read_pbkdf2_params( ASN1_TYPE pbes2_asn, const gnutls_datum* der, 
-       struct pbkdf2_params* params);
-static int read_pbe_enc_params( ASN1_TYPE pbes2_asn, const gnutls_datum* der, 
-       struct pbe_enc_params* params);
-static int decrypt_data( ASN1_TYPE pkcs8_asn, const char* password,
-       const struct pbkdf2_params* kdf_params, const struct pbe_enc_params *enc_params, 
-       gnutls_datum* decrypted_data);
-static ASN1_TYPE decode_private_key_info( const gnutls_datum* der, gnutls_x509_privkey pkey);
-static int write_pbe2_params( ASN1_TYPE pkcs8_asn, const struct pbkdf2_params* kdf_params,
-       const struct pbe_enc_params* enc_params);
-static int encrypt_data( const gnutls_datum* plain, 
-       const struct pbe_enc_params *enc_params, 
-       gnutls_datum* key, gnutls_datum* encrypted);
+static int generate_key(const char *password, unsigned int flags,
+                       struct pbkdf2_params *kdf_params,
+                       struct pbe_enc_params *enc_params,
+                       gnutls_datum * key);
+static int read_pbkdf2_params(ASN1_TYPE pbes2_asn,
+                             const gnutls_datum * der,
+                             struct pbkdf2_params *params);
+static int read_pbe_enc_params(ASN1_TYPE pbes2_asn,
+                              const gnutls_datum * der,
+                              struct pbe_enc_params *params);
+static int decrypt_data(schema_id, ASN1_TYPE pkcs8_asn, const char *root,
+                       const char *password,
+                       const struct pbkdf2_params *kdf_params,
+                       const struct pbe_enc_params *enc_params,
+                       gnutls_datum * decrypted_data);
+static ASN1_TYPE decode_private_key_info(const gnutls_datum * der,
+                                        gnutls_x509_privkey pkey);
+static int write_pbe2_params(ASN1_TYPE pkcs8_asn,
+                            const struct pbkdf2_params *kdf_params,
+                            const struct pbe_enc_params *enc_params);
+static int encrypt_data(const gnutls_datum * plain,
+                       const struct pbe_enc_params *enc_params,
+                       gnutls_datum * key, gnutls_datum * encrypted);
+
+static int read_pkcs12_kdf_params(ASN1_TYPE pbes2_asn,
+                             struct pbkdf2_params *params);
 
 #define PEM_PKCS8 "ENCRYPTED PRIVATE KEY"
 #define PEM_UNENCRYPTED_PKCS8 "PRIVATE KEY"
 
+/* Returns a negative error code if the encryption schema in
+ * the OID is not supported. The schema ID is returned.
+ */
+inline static int check_schema(const char *oid)
+{
+
+       if (strcmp(oid, PBES2_OID) == 0)
+               return PBES2;
+
+       if (strcmp(oid, PBE_3DES_SHA1_OID) == 0)
+               return PKCS12_3DES_SHA1;
+
+       _gnutls_x509_log("PKCS encryption schema OID '%s' is unsupported.\n", oid);
+
+       return GNUTLS_E_UNKNOWN_CIPHER_TYPE;
+}
 
 /* 
  * Encodes a PKCS #1 private key to a PKCS #8 private key
  * info. The output will be allocated and stored into der. Also
  * the ASN1_TYPE of private key info will be returned.
  */
-static int encode_to_private_key_info( gnutls_x509_privkey pkey, gnutls_datum* der, 
-       ASN1_TYPE *pkey_info)
+static int encode_to_private_key_info(gnutls_x509_privkey pkey,
+                                     gnutls_datum * der,
+                                     ASN1_TYPE * pkey_info)
 {
        int result, size;
        opaque *data = NULL;
@@ -98,8 +133,8 @@ static int encode_to_private_key_info( gnutls_x509_privkey pkey, gnutls_datum* d
 
        if ((result =
             asn1_create_element(_gnutls_get_pkix(),
-                                  "PKIX1.pkcs-8-PrivateKeyInfo", pkey_info
-                                  )) != ASN1_SUCCESS) {
+                                "PKIX1.pkcs-8-PrivateKeyInfo",
+                                pkey_info)) != ASN1_SUCCESS) {
                gnutls_assert();
                result = _gnutls_asn2err(result);
                goto error;
@@ -107,7 +142,7 @@ static int encode_to_private_key_info( gnutls_x509_privkey pkey, gnutls_datum* d
 
        /* Write the version.
         */
-       result = asn1_write_value( *pkey_info, "version", &null, 1);
+       result = asn1_write_value(*pkey_info, "version", &null, 1);
        if (result != ASN1_SUCCESS) {
                gnutls_assert();
                result = _gnutls_asn2err(result);
@@ -117,14 +152,18 @@ static int encode_to_private_key_info( gnutls_x509_privkey pkey, gnutls_datum* d
        /* write the privateKeyAlgorithm
         * fields. (OID+NULL data)
         */
-       result = asn1_write_value( *pkey_info, "privateKeyAlgorithm.algorithm", PKIX1_RSA_OID, 1);
+       result =
+           asn1_write_value(*pkey_info, "privateKeyAlgorithm.algorithm",
+                            PKIX1_RSA_OID, 1);
        if (result != ASN1_SUCCESS) {
                gnutls_assert();
                result = _gnutls_asn2err(result);
                goto error;
        }
 
-       result = asn1_write_value( *pkey_info, "privateKeyAlgorithm.parameters", NULL, 0);
+       result =
+           asn1_write_value(*pkey_info, "privateKeyAlgorithm.parameters",
+                            NULL, 0);
        if (result != ASN1_SUCCESS) {
                gnutls_assert();
                result = _gnutls_asn2err(result);
@@ -133,29 +172,33 @@ static int encode_to_private_key_info( gnutls_x509_privkey pkey, gnutls_datum* d
 
        /* Write the raw private key
         */
-       result = gnutls_x509_privkey_export( pkey, GNUTLS_X509_FMT_DER, NULL, &size);
+       result =
+           gnutls_x509_privkey_export(pkey, GNUTLS_X509_FMT_DER, NULL,
+                                      &size);
        if (result != GNUTLS_E_SHORT_MEMORY_BUFFER) {
                gnutls_assert();
                goto error;
        }
 
-       data = gnutls_alloca( size);
+       data = gnutls_alloca(size);
        if (data == NULL) {
                gnutls_assert();
                result = GNUTLS_E_MEMORY_ERROR;
                goto error;
        }
-       
 
-       result = gnutls_x509_privkey_export( pkey, GNUTLS_X509_FMT_DER, data, &size);
+
+       result =
+           gnutls_x509_privkey_export(pkey, GNUTLS_X509_FMT_DER, data,
+                                      &size);
        if (result < 0) {
                gnutls_assert();
                goto error;
        }
 
-       result = asn1_write_value( *pkey_info, "privateKey", data, size);
-       
-       gnutls_afree( data);
+       result = asn1_write_value(*pkey_info, "privateKey", data, size);
+
+       gnutls_afree(data);
        data = NULL;
 
        if (result != ASN1_SUCCESS) {
@@ -166,7 +209,7 @@ static int encode_to_private_key_info( gnutls_x509_privkey pkey, gnutls_datum* d
 
        /* Append an empty Attributes field.
         */
-       result = asn1_write_value( *pkey_info, "attributes", NULL, 0);
+       result = asn1_write_value(*pkey_info, "attributes", NULL, 0);
        if (result != ASN1_SUCCESS) {
                gnutls_assert();
                result = _gnutls_asn2err(result);
@@ -176,7 +219,7 @@ static int encode_to_private_key_info( gnutls_x509_privkey pkey, gnutls_datum* d
        /* DER Encode the generated private key info.
         */
        size = 0;
-       result = asn1_der_coding( *pkey_info, "", NULL, &size, NULL);
+       result = asn1_der_coding(*pkey_info, "", NULL, &size, NULL);
        if (result != ASN1_MEM_ERROR) {
                gnutls_assert();
                result = _gnutls_asn2err(result);
@@ -186,13 +229,13 @@ static int encode_to_private_key_info( gnutls_x509_privkey pkey, gnutls_datum* d
        /* allocate data for the der
         */
        der->size = size;
-       der->data = gnutls_malloc( size);
+       der->data = gnutls_malloc(size);
        if (der->data == NULL) {
                gnutls_assert();
                return GNUTLS_E_MEMORY_ERROR;
        }
 
-       result = asn1_der_coding( *pkey_info, "", der->data, &size, NULL);
+       result = asn1_der_coding(*pkey_info, "", der->data, &size, NULL);
        if (result != ASN1_SUCCESS) {
                gnutls_assert();
                result = _gnutls_asn2err(result);
@@ -201,22 +244,25 @@ static int encode_to_private_key_info( gnutls_x509_privkey pkey, gnutls_datum* d
 
        return 0;
 
-       error:
-               asn1_delete_structure( pkey_info);
-               if (data != NULL) { gnutls_afree(data); }
-               return result;
+      error:
+       asn1_delete_structure(pkey_info);
+       if (data != NULL) {
+               gnutls_afree(data);
+       }
+       return result;
 
 }
 
 /* Converts a PKCS #8 private key info to
  * a PKCS #8 EncryptedPrivateKeyInfo.
  */
-static ASN1_TYPE encode_to_pkcs8_key( const gnutls_datum *raw_key, 
-       const char* password, unsigned int flags)
+static ASN1_TYPE encode_to_pkcs8_key(const gnutls_datum * raw_key,
+                                    const char *password,
+                                    unsigned int flags)
 {
        int result;
-       gnutls_datum key = {NULL, 0};
-       gnutls_datum tmp = {NULL, 0};
+       gnutls_datum key = { NULL, 0 };
+       gnutls_datum tmp = { NULL, 0 };
        ASN1_TYPE pkcs8_asn = ASN1_TYPE_EMPTY;
        struct pbkdf2_params kdf_params;
        struct pbe_enc_params enc_params;
@@ -224,15 +270,17 @@ static ASN1_TYPE encode_to_pkcs8_key( const gnutls_datum *raw_key,
 
        if ((result =
             asn1_create_element(_gnutls_get_pkix(),
-                                  "PKIX1.pkcs-8-EncryptedPrivateKeyInfo", &pkcs8_asn
-                                  )) != ASN1_SUCCESS) {
+                                "PKIX1.pkcs-8-EncryptedPrivateKeyInfo",
+                                &pkcs8_asn)) != ASN1_SUCCESS) {
                gnutls_assert();
                goto error;
        }
 
        /* Write the encryption schema OID
         */
-       result = asn1_write_value( pkcs8_asn, "encryptionAlgorithm.algorithm", PBES2_OID, 1);
+       result =
+           asn1_write_value(pkcs8_asn, "encryptionAlgorithm.algorithm",
+                            PBES2_OID, 1);
        if (result != ASN1_SUCCESS) {
                gnutls_assert();
                goto error;
@@ -240,22 +288,23 @@ static ASN1_TYPE encode_to_pkcs8_key( const gnutls_datum *raw_key,
 
        /* Generate a symmetric key.
         */
-       result = generate_key( password, flags, &kdf_params, &enc_params, &key);
+       result =
+           generate_key(password, flags, &kdf_params, &enc_params, &key);
        if (result < 0) {
                gnutls_assert();
                goto error;
        }
 
-       result = write_pbe2_params( pkcs8_asn, &kdf_params, &enc_params);
+       result = write_pbe2_params(pkcs8_asn, &kdf_params, &enc_params);
        if (result < 0) {
                gnutls_assert();
                goto error;
        }
 
-       /* Parameters have been decoded. Now
-        * decrypt the EncryptedData.
+       /* Parameters have been encoded. Now
+        * encrypt the Data.
         */
-       result = encrypt_data( raw_key, &enc_params, &key, &tmp);
+       result = encrypt_data(raw_key, &enc_params, &key, &tmp);
        if (result < 0) {
                gnutls_assert();
                goto error;
@@ -263,22 +312,24 @@ static ASN1_TYPE encode_to_pkcs8_key( const gnutls_datum *raw_key,
 
        /* write the encrypted data.
         */
-       result = asn1_write_value( pkcs8_asn, "encryptedData", tmp.data, tmp.size);
+       result =
+           asn1_write_value(pkcs8_asn, "encryptedData", tmp.data,
+                            tmp.size);
        if (result != ASN1_SUCCESS) {
                gnutls_assert();
                goto error;
        }
-       
-       _gnutls_free_datum( &tmp);
-       _gnutls_free_datum( &key);
+
+       _gnutls_free_datum(&tmp);
+       _gnutls_free_datum(&key);
 
        return pkcs8_asn;
 
-       error:
-               _gnutls_free_datum( &key);
-               _gnutls_free_datum( &tmp);
-               asn1_delete_structure(&pkcs8_asn);
-               return NULL;
+      error:
+       _gnutls_free_datum(&key);
+       _gnutls_free_datum(&tmp);
+       asn1_delete_structure(&pkcs8_asn);
+       return NULL;
 }
 
 
@@ -305,59 +356,176 @@ static ASN1_TYPE encode_to_pkcs8_key( const gnutls_datum *raw_key,
   * 0 on success.
   *
   **/
-int gnutls_x509_privkey_export_pkcs8( gnutls_x509_privkey key,
-       gnutls_x509_crt_fmt format, const char* password, unsigned int flags,
-       unsigned char* output_data, int* output_data_size)
+int gnutls_x509_privkey_export_pkcs8(gnutls_x509_privkey key,
+                                    gnutls_x509_crt_fmt format,
+                                    const char *password,
+                                    unsigned int flags,
+                                    unsigned char *output_data,
+                                    int *output_data_size)
 {
-ASN1_TYPE pkcs8_asn, pkey_info;
-int ret;
-gnutls_datum tmp;
+       ASN1_TYPE pkcs8_asn, pkey_info;
+       int ret;
+       gnutls_datum tmp;
 
        /* Get the private key info
         */
-       ret = encode_to_private_key_info( key, &tmp, &pkey_info);
-       if ( ret < 0) {
+       ret = encode_to_private_key_info(key, &tmp, &pkey_info);
+       if (ret < 0) {
                gnutls_assert();
                return ret;
        }
 
        if (!(flags & GNUTLS_PKCS8_PLAIN) || password == NULL) {
 
-               asn1_delete_structure( &pkey_info); /* we don't need it */
+               asn1_delete_structure(&pkey_info);      /* we don't need it */
 
-               pkcs8_asn = encode_to_pkcs8_key( &tmp, password, flags);
-               _gnutls_free_datum( &tmp);
+               pkcs8_asn = encode_to_pkcs8_key(&tmp, password, flags);
+               _gnutls_free_datum(&tmp);
 
                if (pkcs8_asn == NULL) {
                        gnutls_assert();
                        return GNUTLS_E_ASN1_GENERIC_ERROR;
                }
-               
-               ret = _gnutls_x509_export_int( pkcs8_asn, format, PEM_PKCS8, *output_data_size,
-                       output_data, output_data_size);
 
-               asn1_delete_structure( &pkcs8_asn);
+               ret =
+                   _gnutls_x509_export_int(pkcs8_asn, format, PEM_PKCS8,
+                                           *output_data_size, output_data,
+                                           output_data_size);
+
+               asn1_delete_structure(&pkcs8_asn);
 
        } else {
-               _gnutls_free_datum( &tmp);
+               _gnutls_free_datum(&tmp);
 
-               ret = _gnutls_x509_export_int( pkey_info, format, PEM_UNENCRYPTED_PKCS8, 
-                       *output_data_size, output_data, output_data_size);
-               
-               asn1_delete_structure( &pkey_info);
+               ret =
+                   _gnutls_x509_export_int(pkey_info, format,
+                                           PEM_UNENCRYPTED_PKCS8,
+                                           *output_data_size, output_data,
+                                           output_data_size);
+
+               asn1_delete_structure(&pkey_info);
        }
 
        return ret;
 }
 
 
+/* Read the parameters cipher, IV, salt etc using the given
+ * schema ID.
+ */
+static
+int read_pkcs_schema_params(schema_id schema, const char* password,
+                       const opaque * data, int data_size,
+                           struct pbkdf2_params *kdf_params,
+                           struct pbe_enc_params *enc_params)
+{
+       ASN1_TYPE pbes2_asn = ASN1_TYPE_EMPTY;
+       int result;
+       gnutls_datum tmp;
+
+       switch (schema) {
+
+       case PBES2:
+
+               /* Now check the key derivation and the encryption
+                * functions.
+                */
+               if ((result =
+                    asn1_create_element(_gnutls_get_pkix(),
+                                        "PKIX1.pkcs-5-PBES2-params",
+                                        &pbes2_asn)) != ASN1_SUCCESS) {
+                       gnutls_assert();
+                       result = _gnutls_asn2err(result);
+                       goto error;
+               }
+
+               /* Decode the parameters.
+                */
+               result =
+                   asn1_der_decoding(&pbes2_asn,
+                                     data, data_size, NULL);
+               if (result != ASN1_SUCCESS) {
+                       gnutls_assert();
+                       result = _gnutls_asn2err(result);
+                       goto error;
+               }
+
+               tmp.data = (opaque*)data;
+               tmp.size = data_size;
+
+               result = read_pbkdf2_params(pbes2_asn, &tmp, kdf_params);
+               if (result < 0) {
+                       gnutls_assert();
+                       result = _gnutls_asn2err(result);
+                       goto error;
+               }
+
+               result = read_pbe_enc_params(pbes2_asn, &tmp, enc_params);
+               if (result < 0) {
+                       gnutls_assert();
+                       result = _gnutls_asn2err(result);
+                       goto error;
+               }
+
+               asn1_delete_structure(&pbes2_asn);
+               return 0;
+               break;
+
+       case PKCS12_3DES_SHA1:
+               if ((result =
+                    asn1_create_element(_gnutls_get_pkix(),
+                                        "PKIX1.pkcs-12-PbeParams",
+                                        &pbes2_asn)) != ASN1_SUCCESS) {
+                       gnutls_assert();
+                       result = _gnutls_asn2err(result);
+                       goto error;
+               }
+
+               /* Decode the parameters.
+                */
+               result =
+                   asn1_der_decoding(&pbes2_asn,
+                                     data, data_size, NULL);
+               if (result != ASN1_SUCCESS) {
+                       gnutls_assert();
+                       result = _gnutls_asn2err(result);
+                       goto error;
+               }
+
+               result = read_pkcs12_kdf_params(pbes2_asn, kdf_params);
+               if (result < 0) {
+                       gnutls_assert();
+                       result = _gnutls_asn2err(result);
+                       goto error;
+               }
+
+               enc_params->cipher = GNUTLS_CIPHER_3DES_CBC;
+               enc_params->iv_size = 8;
+               _pkcs12_string_to_key( 2/*IV*/, kdf_params->salt, kdf_params->salt_size,
+                       kdf_params->iter_count, password, 8, enc_params->iv);
+
+               asn1_delete_structure(&pbes2_asn);
+
+               return 0;
+               break;
+
+       }                       /* switch */
+
+       return GNUTLS_E_UNKNOWN_CIPHER_TYPE;
+
+      error:
+       asn1_delete_structure(&pbes2_asn);
+       return result;
+}
+
 
 /* Converts a PKCS #8 key to
  * an internal structure (gnutls_private_key)
  * (normally a PKCS #1 encoded RSA key)
  */
-static ASN1_TYPE decode_pkcs8_key( const gnutls_datum *raw_key, 
-       const char* password, gnutls_x509_privkey pkey)
+static ASN1_TYPE decode_pkcs8_key(const gnutls_datum * raw_key,
+                                 const char *password,
+                                 gnutls_x509_privkey pkey)
 {
        int result, len;
        opaque enc_oid[64];
@@ -367,17 +535,19 @@ static ASN1_TYPE decode_pkcs8_key( const gnutls_datum *raw_key,
        int params_start, params_end, params_len;
        struct pbkdf2_params kdf_params;
        struct pbe_enc_params enc_params;
-
+       schema_id schema;
 
        if ((result =
             asn1_create_element(_gnutls_get_pkix(),
-                                  "PKIX1.pkcs-8-EncryptedPrivateKeyInfo", &pkcs8_asn
-                                  )) != ASN1_SUCCESS) {
+                                "PKIX1.pkcs-8-EncryptedPrivateKeyInfo",
+                                &pkcs8_asn)) != ASN1_SUCCESS) {
                gnutls_assert();
                goto error;
        }
 
-       result = asn1_der_decoding(&pkcs8_asn, raw_key->data, raw_key->size, NULL);
+       result =
+           asn1_der_decoding(&pkcs8_asn, raw_key->data, raw_key->size,
+                             NULL);
        if (result != ASN1_SUCCESS) {
                gnutls_assert();
                goto error;
@@ -386,71 +556,46 @@ static ASN1_TYPE decode_pkcs8_key( const gnutls_datum *raw_key,
        /* Check the encryption schema OID
         */
        len = sizeof(enc_oid);
-       result = asn1_read_value( pkcs8_asn, "encryptionAlgorithm.algorithm", enc_oid, &len);
+       result =
+           asn1_read_value(pkcs8_asn, "encryptionAlgorithm.algorithm",
+                           enc_oid, &len);
        if (result != ASN1_SUCCESS) {
                gnutls_assert();
                goto error;
        }
 
-       /* we only support PBES2 
-        */
-       if (strcmp( enc_oid, PBES2_OID) != 0) {
+       if ((result = check_schema(enc_oid)) < 0) {
                gnutls_assert();
-               _gnutls_x509_log( "PKCS #8 encryption schema OID '%s' is unsupported.\n", enc_oid);
                goto error;
        }
 
+       schema = result;
+
        /* Get the DER encoding of the parameters.
         */
-       result = asn1_der_decoding_startEnd( pkcs8_asn, raw_key->data, raw_key->size,
-               "encryptionAlgorithm.parameters", &params_start, &params_end);
+       result =
+           asn1_der_decoding_startEnd(pkcs8_asn, raw_key->data,
+                                      raw_key->size,
+                                      "encryptionAlgorithm.parameters",
+                                      &params_start, &params_end);
        if (result != ASN1_SUCCESS) {
                gnutls_assert();
                goto error;
        }
        params_len = params_end - params_start + 1;
 
-       /* Now check the key derivation and the encryption
-        * functions.
-        */
-       if ((result =
-            asn1_create_element(_gnutls_get_pkix(),
-                                  "PKIX1.pkcs-5-PBES2-params", &pbes2_asn
-                                  )) != ASN1_SUCCESS) {
-               gnutls_assert();
-               return NULL;
-       }
-
-       /* Decode the parameters.
-        */
-       result = asn1_der_decoding(&pbes2_asn, &raw_key->data[params_start], params_len, NULL);
-       if (result != ASN1_SUCCESS) {
-               gnutls_assert();
-               goto error;
-       }
-
-       tmp.data = &raw_key->data[params_start];
-       tmp.size = params_len;
-
-       result = read_pbkdf2_params( pbes2_asn, &tmp, &kdf_params);
-       if (result < 0) {
-               gnutls_assert();
-               goto error;
-       }
-
-       result = read_pbe_enc_params( pbes2_asn, &tmp, &enc_params);
-       if (result < 0) {
-               gnutls_assert();
-               goto error;
-       }
-       
-       asn1_delete_structure( &pbes2_asn);
+       result =
+           read_pkcs_schema_params(schema, password,
+                                   &raw_key->data[params_start],
+                                   params_len, &kdf_params, &enc_params);
 
 
        /* Parameters have been decoded. Now
         * decrypt the EncryptedData.
         */
-       result = decrypt_data( pkcs8_asn, password, &kdf_params, &enc_params, &tmp);
+       result =
+           decrypt_data(schema, pkcs8_asn, "encryptedData", password, &kdf_params,
+                        &enc_params, &tmp);
        if (result < 0) {
                gnutls_assert();
                goto error;
@@ -458,18 +603,19 @@ static ASN1_TYPE decode_pkcs8_key( const gnutls_datum *raw_key,
 
        asn1_delete_structure(&pkcs8_asn);
 
-       ret_asn = decode_private_key_info( &tmp, pkey);
-       _gnutls_free_datum( &tmp);
+       ret_asn = decode_private_key_info(&tmp, pkey);
+       _gnutls_free_datum(&tmp);
 
        return ret_asn;
 
-       error:
-               asn1_delete_structure(&pbes2_asn);
-               asn1_delete_structure(&pkcs8_asn);
-               return NULL;
+      error:
+       asn1_delete_structure(&pbes2_asn);
+       asn1_delete_structure(&pkcs8_asn);
+       return NULL;
 }
 
-static ASN1_TYPE decode_private_key_info( const gnutls_datum* der, gnutls_x509_privkey pkey)
+static ASN1_TYPE decode_private_key_info(const gnutls_datum * der,
+                                        gnutls_x509_privkey pkey)
 {
        int result, len;
        opaque oid[64], *data = NULL;
@@ -481,8 +627,8 @@ static ASN1_TYPE decode_private_key_info( const gnutls_datum* der, gnutls_x509_p
 
        if ((result =
             asn1_create_element(_gnutls_get_pkix(),
-                                  "PKIX1.pkcs-8-PrivateKeyInfo", &pkcs8_asn
-                                  )) != ASN1_SUCCESS) {
+                                "PKIX1.pkcs-8-PrivateKeyInfo",
+                                &pkcs8_asn)) != ASN1_SUCCESS) {
                gnutls_assert();
                goto error;
        }
@@ -496,7 +642,9 @@ static ASN1_TYPE decode_private_key_info( const gnutls_datum* der, gnutls_x509_p
        /* Check the private key algorithm OID
         */
        len = sizeof(oid);
-       result = asn1_read_value( pkcs8_asn, "privateKeyAlgorithm.algorithm", oid, &len);
+       result =
+           asn1_read_value(pkcs8_asn, "privateKeyAlgorithm.algorithm",
+                           oid, &len);
        if (result != ASN1_SUCCESS) {
                gnutls_assert();
                goto error;
@@ -504,54 +652,60 @@ static ASN1_TYPE decode_private_key_info( const gnutls_datum* der, gnutls_x509_p
 
        /* we only support RSA private keys.
         */
-       if (strcmp( oid, PKIX1_RSA_OID) != 0) {
+       if (strcmp(oid, PKIX1_RSA_OID) != 0) {
                gnutls_assert();
-               _gnutls_x509_log( "PKCS #8 private key OID '%s' is unsupported.\n", oid);
+               _gnutls_x509_log
+                   ("PKCS #8 private key OID '%s' is unsupported.\n",
+                    oid);
                goto error;
        }
 
        /* Get the DER encoding of the actual private key.
         */
        data_size = 0;
-       result = asn1_read_value( pkcs8_asn, "privateKey", NULL, &data_size);
+       result =
+           asn1_read_value(pkcs8_asn, "privateKey", NULL, &data_size);
        if (result != ASN1_MEM_ERROR) {
                gnutls_assert();
                result = _gnutls_asn2err(result);
                goto error;
        }
 
-       data = gnutls_alloca( data_size);
+       data = gnutls_alloca(data_size);
        if (data == NULL) {
                gnutls_assert();
                result = GNUTLS_E_MEMORY_ERROR;
                goto error;
        }
 
-       result = asn1_read_value( pkcs8_asn, "privateKey", data, &data_size);
+       result =
+           asn1_read_value(pkcs8_asn, "privateKey", data, &data_size);
        if (result != ASN1_SUCCESS) {
                gnutls_assert();
                result = _gnutls_asn2err(result);
                goto error;
        }
 
-       asn1_delete_structure( &pkcs8_asn);
-       
+       asn1_delete_structure(&pkcs8_asn);
+
        tmp.data = data;
        tmp.size = data_size;
-       
+
        pkey->pk_algorithm = GNUTLS_PK_RSA;
 
-       ret_asn = _gnutls_privkey_decode_pkcs1_rsa_key( &tmp, pkey);
-       if (ret_asn==NULL) {
+       ret_asn = _gnutls_privkey_decode_pkcs1_rsa_key(&tmp, pkey);
+       if (ret_asn == NULL) {
                gnutls_assert();
        }
-       
+
        return ret_asn;
 
-       error:
-               asn1_delete_structure( &pkcs8_asn);
-               if (data != NULL) { gnutls_afree(data); }
-               return NULL;
+      error:
+       asn1_delete_structure(&pkcs8_asn);
+       if (data != NULL) {
+               gnutls_afree(data);
+       }
+       return NULL;
 
 }
 
@@ -574,8 +728,11 @@ static ASN1_TYPE decode_private_key_info( const gnutls_datum* der, gnutls_x509_p
   * Returns 0 on success.
   *
   **/
-int gnutls_x509_privkey_import_pkcs8(gnutls_x509_privkey key, const gnutls_datum * data,
-       gnutls_x509_crt_fmt format, const char * password, unsigned int flags)
+int gnutls_x509_privkey_import_pkcs8(gnutls_x509_privkey key,
+                                    const gnutls_datum * data,
+                                    gnutls_x509_crt_fmt format,
+                                    const char *password,
+                                    unsigned int flags)
 {
        int result = 0, need_free = 0;
        gnutls_datum _data = { data->data, data->size };
@@ -590,34 +747,37 @@ int gnutls_x509_privkey_import_pkcs8(gnutls_x509_privkey key, const gnutls_datum
 
                /* Try the first header 
                 */
-               result = _gnutls_fbase64_decode(PEM_UNENCRYPTED_PKCS8, data->data, data->size,
-                       &out);
+               result =
+                   _gnutls_fbase64_decode(PEM_UNENCRYPTED_PKCS8,
+                                          data->data, data->size, &out);
                encrypted = 0;
-               
-               if (result < 0) { /* Try the encrypted header 
-                                  */
-                       result = _gnutls_fbase64_decode(PEM_PKCS8, data->data, data->size,
-                               &out);
+
+               if (result < 0) {       /* Try the encrypted header 
+                                        */
+                       result =
+                           _gnutls_fbase64_decode(PEM_PKCS8, data->data,
+                                                  data->size, &out);
 
                        if (result <= 0) {
-                               if (result==0) result = GNUTLS_E_INTERNAL_ERROR;
+                               if (result == 0)
+                                       result = GNUTLS_E_INTERNAL_ERROR;
                                gnutls_assert();
                                return result;
                        }
-                       
+
                        encrypted = 1;
                }
 
                _data.data = out;
                _data.size = result;
-               
+
                need_free = 1;
        }
 
        if (flags & GNUTLS_PKCS8_PLAIN || password == NULL) {
-               key->key = decode_private_key_info( &_data, key);
-       } else { /* encrypted. */
-               key->key = decode_pkcs8_key( &_data, password, key);
+               key->key = decode_private_key_info(&_data, key);
+       } else {                /* encrypted. */
+               key->key = decode_pkcs8_key(&_data, password, key);
        }
 
        if (key->key == NULL) {
@@ -626,7 +786,8 @@ int gnutls_x509_privkey_import_pkcs8(gnutls_x509_privkey key, const gnutls_datum
                goto cleanup;
        }
 
-       if (need_free) _gnutls_free_datum( &_data);
+       if (need_free)
+               _gnutls_free_datum(&_data);
 
        /* The key has now been decoded.
         */
@@ -634,41 +795,49 @@ int gnutls_x509_privkey_import_pkcs8(gnutls_x509_privkey key, const gnutls_datum
        return 0;
 
       cleanup:
-       key->pk_algorithm = GNUTLS_PK_UNKNOWN;
-       if (need_free) _gnutls_free_datum( &_data);
+       key->pk_algorithm = GNUTLS_PK_UNKNOWN;
+       if (need_free)
+               _gnutls_free_datum(&_data);
        return result;
 }
 
 /* Reads the PBKDF2 parameters.
  */
-static int read_pbkdf2_params( ASN1_TYPE pbes2_asn, const gnutls_datum* der, 
-       struct pbkdf2_params* params)
+static int read_pbkdf2_params(ASN1_TYPE pbes2_asn,
+                             const gnutls_datum * der,
+                             struct pbkdf2_params *params)
 {
-int params_start, params_end;
-int params_len, len, result;
-ASN1_TYPE pbkdf2_asn = ASN1_TYPE_EMPTY;
-char oid[64];
+       int params_start, params_end;
+       int params_len, len, result;
+       ASN1_TYPE pbkdf2_asn = ASN1_TYPE_EMPTY;
+       char oid[64];
 
-       memset( params, 0, sizeof(params));
+       memset(params, 0, sizeof(params));
 
        /* Check the key derivation algorithm
         */
        len = sizeof(oid);
-       result = asn1_read_value( pbes2_asn, "keyDerivationFunc.algorithm", oid, &len);
+       result =
+           asn1_read_value(pbes2_asn, "keyDerivationFunc.algorithm", oid,
+                           &len);
        if (result != ASN1_SUCCESS) {
                gnutls_assert();
                return _gnutls_asn2err(result);
        }
-       _gnutls_hard_log( "keyDerivationFunc.algorithm: %s\n", oid);
-       
-       if (strcmp( oid, PBKDF2_OID) != 0) {
+       _gnutls_hard_log("keyDerivationFunc.algorithm: %s\n", oid);
+
+       if (strcmp(oid, PBKDF2_OID) != 0) {
                gnutls_assert();
-               _gnutls_x509_log( "PKCS #8 key derivation OID '%s' is unsupported.\n", oid);
+               _gnutls_x509_log
+                   ("PKCS #8 key derivation OID '%s' is unsupported.\n",
+                    oid);
                return _gnutls_asn2err(result);
        }
 
-       result = asn1_der_decoding_startEnd( pbes2_asn, der->data, der->size,
-               "keyDerivationFunc.parameters", &params_start, &params_end);
+       result =
+           asn1_der_decoding_startEnd(pbes2_asn, der->data, der->size,
+                                      "keyDerivationFunc.parameters",
+                                      &params_start, &params_end);
        if (result != ASN1_SUCCESS) {
                gnutls_assert();
                return _gnutls_asn2err(result);
@@ -680,13 +849,15 @@ char oid[64];
         */
        if ((result =
             asn1_create_element(_gnutls_get_pkix(),
-                                  "PKIX1.pkcs-5-PBKDF2-params", &pbkdf2_asn
-                                  )) != ASN1_SUCCESS) {
+                                "PKIX1.pkcs-5-PBKDF2-params",
+                                &pbkdf2_asn)) != ASN1_SUCCESS) {
                gnutls_assert();
                return _gnutls_asn2err(result);
        }
-       
-       result = asn1_der_decoding(&pbkdf2_asn, &der->data[params_start], params_len, NULL);
+
+       result =
+           asn1_der_decoding(&pbkdf2_asn, &der->data[params_start],
+                             params_len, NULL);
        if (result != ASN1_SUCCESS) {
                gnutls_assert();
                result = _gnutls_asn2err(result);
@@ -695,73 +866,146 @@ char oid[64];
 
        /* read the salt */
        params->salt_size = sizeof(params->salt);
-       result = asn1_read_value( pbkdf2_asn, "salt.specified", params->salt, &params->salt_size);
+       result =
+           asn1_read_value(pbkdf2_asn, "salt.specified", params->salt,
+                           &params->salt_size);
        if (result != ASN1_SUCCESS) {
                gnutls_assert();
                result = _gnutls_asn2err(result);
                goto error;
        }
-       _gnutls_hard_log( "salt.specified.size: %d\n", params->salt_size);
+       _gnutls_hard_log("salt.specified.size: %d\n", params->salt_size);
 
        /* read the iteration count 
         */
        len = sizeof(oid);
-       result = _gnutls_x509_read_ui( pbkdf2_asn, "iterationCount", oid, len, &params->iter_count);
+       result =
+           _gnutls_x509_read_ui(pbkdf2_asn, "iterationCount", oid, len,
+                                &params->iter_count);
        if (result != ASN1_SUCCESS) {
                gnutls_assert();
                goto error;
        }
-       _gnutls_hard_log( "iterationCount: %d\n", params->iter_count);
+       _gnutls_hard_log("iterationCount: %d\n", params->iter_count);
 
        /* read the keylength, if it is set.
         */
        len = sizeof(oid);
 
-       result = _gnutls_x509_read_ui( pbkdf2_asn, "keyLength", oid, len, &params->key_size);
+       result =
+           _gnutls_x509_read_ui(pbkdf2_asn, "keyLength", oid, len,
+                                &params->key_size);
        if (result < 0) {
                params->key_size = 0;
        }
-       _gnutls_hard_log( "keyLength: %d\n", params->key_size);
-       
+       _gnutls_hard_log("keyLength: %d\n", params->key_size);
+
        /* We don't read the PRF. We only use the default.
         */
 
        return 0;
 
-       error:
-               asn1_delete_structure( &pbkdf2_asn);
-               return result;
+      error:
+       asn1_delete_structure(&pbkdf2_asn);
+       return result;
 
 }
 
-static int read_pbe_enc_params( ASN1_TYPE pbes2_asn, const gnutls_datum* der, 
-       struct pbe_enc_params* params)
+/* Reads the PBE parameters from PKCS-12 schemas (*&#%*&#% RSA).
+ */
+static int read_pkcs12_kdf_params(ASN1_TYPE pbes2_asn,
+                             struct pbkdf2_params *params)
 {
-int params_start, params_end;
-int params_len, len, result;
-ASN1_TYPE pbe_asn = ASN1_TYPE_EMPTY;
-char oid[64];
+       int len, result;
+       char oid[64];
+
+       memset(params, 0, sizeof(params));
+
+       /* read the salt */
+       params->salt_size = sizeof(params->salt);
+       result =
+           asn1_read_value(pbes2_asn, "salt", params->salt,
+                           &params->salt_size);
+       if (result != ASN1_SUCCESS) {
+               gnutls_assert();
+               result = _gnutls_asn2err(result);
+               goto error;
+       }
+       _gnutls_hard_log("salt.size: %d\n", params->salt_size);
+
+       /* read the iteration count 
+        */
+       len = sizeof(oid);
+       result =
+           _gnutls_x509_read_ui(pbes2_asn, "iterations", oid, len,
+                                &params->iter_count);
+       if (result != ASN1_SUCCESS) {
+               gnutls_assert();
+               goto error;
+       }
+       _gnutls_hard_log("iterationCount: %d\n", params->iter_count);
 
-       memset( params, 0, sizeof(params));
+       params->key_size = 0;
+
+       return 0;
+
+      error:
+       return result;
+
+}
+
+
+/* Converts an OID to a gnutls cipher type.
+ */
+inline
+    static int oid2cipher(const char *oid, gnutls_cipher_algorithm * algo)
+{
+
+       *algo = 0;
+
+       if (strcmp(oid, DES_EDE3_CBC_OID) == 0) {
+               *algo = GNUTLS_CIPHER_3DES_CBC;
+               return 0;
+       }
+
+       _gnutls_x509_log("PKCS #8 encryption OID '%s' is unsupported.\n",
+                        oid);
+       return GNUTLS_E_UNKNOWN_CIPHER_TYPE;
+}
+
+
+static int read_pbe_enc_params(ASN1_TYPE pbes2_asn,
+                              const gnutls_datum * der,
+                              struct pbe_enc_params *params)
+{
+       int params_start, params_end;
+       int params_len, len, result;
+       ASN1_TYPE pbe_asn = ASN1_TYPE_EMPTY;
+       char oid[64];
+
+       memset(params, 0, sizeof(params));
 
        /* Check the encryption algorithm
         */
        len = sizeof(oid);
-       result = asn1_read_value( pbes2_asn, "encryptionScheme.algorithm", oid, &len);
+       result =
+           asn1_read_value(pbes2_asn, "encryptionScheme.algorithm", oid,
+                           &len);
        if (result != ASN1_SUCCESS) {
                gnutls_assert();
                goto error;
        }
-       _gnutls_hard_log( "encryptionScheme.algorithm: %s\n", oid);
-       
-       if (strcmp( oid, DES_EDE3_CBC_OID) != 0) {
+       _gnutls_hard_log("encryptionScheme.algorithm: %s\n", oid);
+
+       if ((result = oid2cipher(oid, &params->cipher)) < 0) {
                gnutls_assert();
-               _gnutls_x509_log( "PKCS #8 encryption OID '%s' is unsupported.\n", oid);
                goto error;
        }
 
-       result = asn1_der_decoding_startEnd( pbes2_asn, der->data, der->size,
-               "encryptionScheme.parameters", &params_start, &params_end);
+       result =
+           asn1_der_decoding_startEnd(pbes2_asn, der->data, der->size,
+                                      "encryptionScheme.parameters",
+                                      &params_start, &params_end);
        if (result != ASN1_SUCCESS) {
                gnutls_assert();
                return _gnutls_asn2err(result);
@@ -772,13 +1016,15 @@ char oid[64];
         */
        if ((result =
             asn1_create_element(_gnutls_get_pkix(),
-                                  "PKIX1.pkcs-5-des-EDE3-CBC-params", &pbe_asn
-                                  )) != ASN1_SUCCESS) {
+                                "PKIX1.pkcs-5-des-EDE3-CBC-params",
+                                &pbe_asn)) != ASN1_SUCCESS) {
                gnutls_assert();
                return _gnutls_asn2err(result);
        }
-       
-       result = asn1_der_decoding(&pbe_asn, &der->data[params_start], params_len, NULL);
+
+       result =
+           asn1_der_decoding(&pbe_asn, &der->data[params_start],
+                             params_len, NULL);
        if (result != ASN1_SUCCESS) {
                gnutls_assert();
                result = _gnutls_asn2err(result);
@@ -787,47 +1033,50 @@ char oid[64];
 
        /* read the IV */
        params->iv_size = sizeof(params->iv);
-       result = asn1_read_value( pbe_asn, "", params->iv, &params->iv_size);
+       result =
+           asn1_read_value(pbe_asn, "", params->iv, &params->iv_size);
        if (result != ASN1_SUCCESS) {
                gnutls_assert();
                result = _gnutls_asn2err(result);
                goto error;
        }
-       _gnutls_hard_log( "IV.size: %d\n", params->iv_size);
+       _gnutls_hard_log("IV.size: %d\n", params->iv_size);
 
        return 0;
 
-       error:
-               asn1_delete_structure( &pbe_asn);
-               return result;
+      error:
+       asn1_delete_structure(&pbe_asn);
+       return result;
 
 }
 
-static int decrypt_data( ASN1_TYPE pkcs8_asn, const char* password,
-       const struct pbkdf2_params *kdf_params, const struct pbe_enc_params *enc_params, 
-       gnutls_datum* decrypted_data)
+static int decrypt_data(schema_id schema, ASN1_TYPE pkcs8_asn, const char *root,
+                       const char *password,
+                       const struct pbkdf2_params *kdf_params,
+                       const struct pbe_enc_params *enc_params,
+                       gnutls_datum * decrypted_data)
 {
-int result;
-int data_size;
-opaque* data=NULL, *key = NULL;
-gnutls_datum dkey, div;
-GNUTLS_CIPHER_HANDLE ch = NULL;
-int key_size;
+       int result;
+       int data_size;
+       opaque *data = NULL, *key = NULL;
+       gnutls_datum dkey, div;
+       GNUTLS_CIPHER_HANDLE ch = NULL;
+       int key_size;
 
        data_size = 0;
-       result = asn1_read_value( pkcs8_asn, "encryptedData", NULL, &data_size);
+       result = asn1_read_value(pkcs8_asn, root, NULL, &data_size);
        if (result != ASN1_MEM_ERROR) {
                gnutls_assert();
                return _gnutls_asn2err(result);
        }
 
-       data = gnutls_malloc( data_size);
+       data = gnutls_malloc(data_size);
        if (data == NULL) {
                gnutls_assert();
                return GNUTLS_E_MEMORY_ERROR;
        }
 
-       result = asn1_read_value( pkcs8_asn, "encryptedData", data, &data_size);
+       result = asn1_read_value(pkcs8_asn, root, data, &data_size);
        if (result != ASN1_SUCCESS) {
                gnutls_assert();
                result = _gnutls_asn2err(result);
@@ -835,11 +1084,12 @@ int key_size;
        }
 
        if (kdf_params->key_size == 0) {
-               key_size = gnutls_cipher_get_key_size( GNUTLS_CIPHER_3DES_CBC);
-       } else key_size = kdf_params->key_size;
+               key_size = gnutls_cipher_get_key_size(enc_params->cipher);
+       } else
+               key_size = kdf_params->key_size;
 
-       key = gnutls_alloca( key_size);
-       if ( key == NULL) {
+       key = gnutls_alloca(key_size);
+       if (key == NULL) {
                gnutls_assert();
                result = GNUTLS_E_MEMORY_ERROR;
                goto error;
@@ -847,26 +1097,39 @@ int key_size;
 
        /* generate the key
         */
-       result = _gnutls_pkcs5_pbkdf2( PKCS5_PRF_SHA1, password, strlen(password),
-               kdf_params->salt, kdf_params->salt_size, kdf_params->iter_count,
-               key_size, key);
+       if (schema == PBES2) {
+               result =
+                   _gnutls_pkcs5_pbkdf2(PKCS5_PRF_SHA1, password,
+                                        strlen(password), kdf_params->salt,
+                                        kdf_params->salt_size,
+                                        kdf_params->iter_count, key_size, key);
        
-       if (result != PKCS5_OK) {
-               gnutls_assert();
-               result = GNUTLS_E_DECRYPTION_FAILED;
-               goto error;
+               if (result != PKCS5_OK) {
+                       gnutls_assert();
+                       result = GNUTLS_E_DECRYPTION_FAILED;
+                       goto error;
+               }
+       } else {
+               result = 
+                       _pkcs12_string_to_key( 1/*KEY*/, kdf_params->salt, kdf_params->salt_size,
+                               kdf_params->iter_count, password, key_size, key);
+               
+               if (result < 0) {
+                       gnutls_assert();
+                       goto error;
+               }
        }
 
        /* do the decryption.
         */
        dkey.data = key;
        dkey.size = key_size;
-       
-       div.data = (opaque*) enc_params->iv;
+
+       div.data = (opaque *) enc_params->iv;
        div.size = enc_params->iv_size;
-       ch = _gnutls_cipher_init( GNUTLS_CIPHER_3DES_CBC, &dkey, &div);
+       ch = _gnutls_cipher_init(enc_params->cipher, &dkey, &div);
 
-       gnutls_afree( key);
+       gnutls_afree(key);
        key = NULL;
 
        if (ch == NULL) {
@@ -874,97 +1137,103 @@ int key_size;
                result = GNUTLS_E_DECRYPTION_FAILED;
                goto error;
        }
-       
-       result = _gnutls_cipher_decrypt( ch, data, data_size);
+
+       result = _gnutls_cipher_decrypt(ch, data, data_size);
        if (result < 0) {
                gnutls_assert();
                goto error;
        }
 
        decrypted_data->data = data;
-       decrypted_data->size = data_size - data[data_size-1];
+       decrypted_data->size = data_size - data[data_size - 1];
 
-       _gnutls_cipher_deinit( ch);
+       _gnutls_cipher_deinit(ch);
 
        return 0;
 
-       error:
-               gnutls_free(data);
-               gnutls_afree(key);
-               if (ch!=NULL)
-                       _gnutls_cipher_deinit( ch);
-               return result;
+      error:
+       gnutls_free(data);
+       gnutls_afree(key);
+       if (ch != NULL)
+               _gnutls_cipher_deinit(ch);
+       return result;
 }
 
 
 /* Writes the PBKDF2 parameters.
  */
-static int write_pbkdf2_params( ASN1_TYPE pbes2_asn, const struct pbkdf2_params *kdf_params)
+static int write_pbkdf2_params(ASN1_TYPE pbes2_asn,
+                              const struct pbkdf2_params *kdf_params)
 {
-int result;
-ASN1_TYPE pbkdf2_asn = ASN1_TYPE_EMPTY;
-char tmp[64];
+       int result;
+       ASN1_TYPE pbkdf2_asn = ASN1_TYPE_EMPTY;
+       char tmp[64];
 
        /* Write the key derivation algorithm
         */
-       result = asn1_write_value( pbes2_asn, "keyDerivationFunc.algorithm", PBKDF2_OID, 1);
+       result =
+           asn1_write_value(pbes2_asn, "keyDerivationFunc.algorithm",
+                            PBKDF2_OID, 1);
        if (result != ASN1_SUCCESS) {
                gnutls_assert();
                return _gnutls_asn2err(result);
        }
-       
+
        /* Now write the key derivation and the encryption
         * functions.
         */
        if ((result =
             asn1_create_element(_gnutls_get_pkix(),
-                                  "PKIX1.pkcs-5-PBKDF2-params", &pbkdf2_asn
-                                  )) != ASN1_SUCCESS) {
+                                "PKIX1.pkcs-5-PBKDF2-params",
+                                &pbkdf2_asn)) != ASN1_SUCCESS) {
                gnutls_assert();
                return _gnutls_asn2err(result);
        }
 
-       result = asn1_write_value( pbkdf2_asn, "salt", "specified", 1);
+       result = asn1_write_value(pbkdf2_asn, "salt", "specified", 1);
        if (result != ASN1_SUCCESS) {
                gnutls_assert();
                result = _gnutls_asn2err(result);
                goto error;
        }
-       
+
        /* write the salt 
         */
-       result = asn1_write_value( pbkdf2_asn, "salt.specified", kdf_params->salt, kdf_params->salt_size);
+       result =
+           asn1_write_value(pbkdf2_asn, "salt.specified",
+                            kdf_params->salt, kdf_params->salt_size);
        if (result != ASN1_SUCCESS) {
                gnutls_assert();
                result = _gnutls_asn2err(result);
                goto error;
        }
-       _gnutls_hard_log( "salt.specified.size: %d\n", kdf_params->salt_size);
+       _gnutls_hard_log("salt.specified.size: %d\n",
+                        kdf_params->salt_size);
 
        /* write the iteration count 
         */
-       _gnutls_write_uint32( kdf_params->iter_count, tmp);
+       _gnutls_write_uint32(kdf_params->iter_count, tmp);
 
-       result = asn1_write_value( pbkdf2_asn, "iterationCount", tmp, 4);
+       result = asn1_write_value(pbkdf2_asn, "iterationCount", tmp, 4);
        if (result != ASN1_SUCCESS) {
                gnutls_assert();
                result = _gnutls_asn2err(result);
                goto error;
        }
-       _gnutls_hard_log( "iterationCount: %d\n", kdf_params->iter_count);
+       _gnutls_hard_log("iterationCount: %d\n", kdf_params->iter_count);
 
        /* write the keylength, if it is set.
         */
-       result = asn1_write_value( pbkdf2_asn, "keyLength", NULL, 0);
+       result = asn1_write_value(pbkdf2_asn, "keyLength", NULL, 0);
        if (result != ASN1_SUCCESS) {
                gnutls_assert();
                result = _gnutls_asn2err(result);
                goto error;
        }
-       
+
        /* We write an emptry prf.
         */
-       result = asn1_write_value( pbkdf2_asn, "prf", NULL, 0);
+       result = asn1_write_value(pbkdf2_asn, "prf", NULL, 0);
        if (result != ASN1_SUCCESS) {
                gnutls_assert();
                result = _gnutls_asn2err(result);
@@ -974,8 +1243,9 @@ char tmp[64];
        /* now encode them an put the DER output
         * in the keyDerivationFunc.parameters
         */
-       result = _gnutls_x509_der_encode_and_copy( pbkdf2_asn, "", 
-               pbes2_asn, "keyDerivationFunc.parameters");
+       result = _gnutls_x509_der_encode_and_copy(pbkdf2_asn, "",
+                                                 pbes2_asn,
+                                                 "keyDerivationFunc.parameters");
        if (result < 0) {
                gnutls_assert();
                goto error;
@@ -983,50 +1253,56 @@ char tmp[64];
 
        return 0;
 
-       error:
-               asn1_delete_structure( &pbkdf2_asn);
-               return result;
+      error:
+       asn1_delete_structure(&pbkdf2_asn);
+       return result;
 
 }
 
-static int write_pbe_enc_params( ASN1_TYPE pbes2_asn, const struct pbe_enc_params* params)
+static int write_pbe_enc_params(ASN1_TYPE pbes2_asn,
+                               const struct pbe_enc_params *params)
 {
-int result;
-ASN1_TYPE pbe_asn = ASN1_TYPE_EMPTY;
+       int result;
+       ASN1_TYPE pbe_asn = ASN1_TYPE_EMPTY;
 
        /* Write the encryption algorithm
         */
-       result = asn1_write_value( pbes2_asn, "encryptionScheme.algorithm", DES_EDE3_CBC_OID, 1);
+       result =
+           asn1_write_value(pbes2_asn, "encryptionScheme.algorithm",
+                            DES_EDE3_CBC_OID, 1);
        if (result != ASN1_SUCCESS) {
                gnutls_assert();
                goto error;
        }
-       _gnutls_hard_log( "encryptionScheme.algorithm: %s\n", DES_EDE3_CBC_OID);
-       
+       _gnutls_hard_log("encryptionScheme.algorithm: %s\n",
+                        DES_EDE3_CBC_OID);
+
        /* Now check the encryption parameters.
         */
        if ((result =
             asn1_create_element(_gnutls_get_pkix(),
-                                  "PKIX1.pkcs-5-des-EDE3-CBC-params", &pbe_asn
-                                  )) != ASN1_SUCCESS) {
+                                "PKIX1.pkcs-5-des-EDE3-CBC-params",
+                                &pbe_asn)) != ASN1_SUCCESS) {
                gnutls_assert();
                return _gnutls_asn2err(result);
        }
-       
+
        /* read the salt */
-       result = asn1_write_value( pbe_asn, "", params->iv, params->iv_size);
+       result =
+           asn1_write_value(pbe_asn, "", params->iv, params->iv_size);
        if (result != ASN1_SUCCESS) {
                gnutls_assert();
                result = _gnutls_asn2err(result);
                goto error;
        }
-       _gnutls_hard_log( "IV.size: %d\n", params->iv_size);
+       _gnutls_hard_log("IV.size: %d\n", params->iv_size);
 
        /* now encode them an put the DER output
         * in the encryptionScheme.parameters
         */
-       result = _gnutls_x509_der_encode_and_copy( pbe_asn, "", 
-               pbes2_asn, "encryptionScheme.parameters");
+       result = _gnutls_x509_der_encode_and_copy(pbe_asn, "",
+                                                 pbes2_asn,
+                                                 "encryptionScheme.parameters");
        if (result < 0) {
                gnutls_assert();
                goto error;
@@ -1034,38 +1310,46 @@ ASN1_TYPE pbe_asn = ASN1_TYPE_EMPTY;
 
        return 0;
 
-       error:
-               asn1_delete_structure( &pbe_asn);
-               return result;
+      error:
+       asn1_delete_structure(&pbe_asn);
+       return result;
 
 }
 
 /* Generates a key and also stores the key parameters.
  */
-static int generate_key( const char* password, unsigned int flags,
-       struct pbkdf2_params* kdf_params, 
-       struct pbe_enc_params* enc_params, gnutls_datum * key)
+static int generate_key(const char *password, unsigned int flags,
+                       struct pbkdf2_params *kdf_params,
+                       struct pbe_enc_params *enc_params,
+                       gnutls_datum * key)
 {
-opaque rnd[2];
-int ret;
+       opaque rnd[2];
+       int ret;
 
        /* We should use the flags here to use different
         * encryption algorithms etc. 
         */
 
-       _gnutls_get_random( rnd, 2, GNUTLS_STRONG_RANDOM);
+       enc_params->cipher = GNUTLS_CIPHER_3DES_CBC;    /* The default */
+
+       _gnutls_get_random(rnd, 2, GNUTLS_STRONG_RANDOM);
 
        /* generate salt */
-       kdf_params->salt_size = GMIN( sizeof( kdf_params->salt), (uint)(10 + (rnd[1] % 10)));
-       _gnutls_get_random( kdf_params->salt, kdf_params->salt_size, GNUTLS_STRONG_RANDOM);
-       
+       kdf_params->salt_size =
+           GMIN(sizeof(kdf_params->salt), (uint) (10 + (rnd[1] % 10)));
+       _gnutls_get_random(kdf_params->salt, kdf_params->salt_size,
+                          GNUTLS_STRONG_RANDOM);
+
        kdf_params->iter_count = 256 + rnd[0];
-       key->size = kdf_params->key_size = gnutls_cipher_get_key_size( GNUTLS_CIPHER_3DES_CBC); /* for 3 DES */
+       key->size = kdf_params->key_size =
+           gnutls_cipher_get_key_size(enc_params->cipher);
 
-       enc_params->iv_size = 8; /* ok for 3DES */
-       _gnutls_get_random( enc_params->iv, enc_params->iv_size, GNUTLS_WEAK_RANDOM);
+       enc_params->iv_size =
+           _gnutls_cipher_get_iv_size(enc_params->cipher);
+       _gnutls_get_random(enc_params->iv, enc_params->iv_size,
+                          GNUTLS_WEAK_RANDOM);
 
-       key->data = gnutls_secure_malloc( key->size);
+       key->data = gnutls_secure_malloc(key->size);
        if (key->data == NULL) {
                gnutls_assert();
                return GNUTLS_E_MEMORY_ERROR;
@@ -1073,14 +1357,16 @@ int ret;
 
        /* now generate the key. 
         */
-       ret = _gnutls_pkcs5_pbkdf2 ( PKCS5_PRF_SHA1, password,
-               strlen(password), kdf_params->salt, kdf_params->salt_size,
-               kdf_params->iter_count, kdf_params->key_size, key->data);
+       ret = _gnutls_pkcs5_pbkdf2(PKCS5_PRF_SHA1, password,
+                                  strlen(password), kdf_params->salt,
+                                  kdf_params->salt_size,
+                                  kdf_params->iter_count,
+                                  kdf_params->key_size, key->data);
        if (ret != PKCS5_OK) {
                gnutls_assert();
                return GNUTLS_E_ENCRYPTION_FAILED;
        }
-       
+
        return 0;
 }
 
@@ -1088,76 +1374,79 @@ int ret;
 /* Encodes the parameters to be written in the encryptionAlgorithm.parameters
  * part.
  */
-static int write_pbe2_params( ASN1_TYPE pkcs8_asn, const struct pbkdf2_params* kdf_params,
-       const struct pbe_enc_params* enc_params)
+static int write_pbe2_params(ASN1_TYPE pkcs8_asn,
+                            const struct pbkdf2_params *kdf_params,
+                            const struct pbe_enc_params *enc_params)
 {
-int result;
-ASN1_TYPE pbes2_asn = ASN1_TYPE_EMPTY;
+       int result;
+       ASN1_TYPE pbes2_asn = ASN1_TYPE_EMPTY;
 
        if ((result =
             asn1_create_element(_gnutls_get_pkix(),
-                                  "PKIX1.pkcs-5-PBES2-params", &pbes2_asn
-                                  )) != ASN1_SUCCESS) {
+                                "PKIX1.pkcs-5-PBES2-params",
+                                &pbes2_asn)) != ASN1_SUCCESS) {
                gnutls_assert();
                return _gnutls_asn2err(result);
        }
 
-       result = write_pbkdf2_params( pbes2_asn, kdf_params);
+       result = write_pbkdf2_params(pbes2_asn, kdf_params);
        if (result < 0) {
                gnutls_assert();
                goto error;
        }
 
-       result = write_pbe_enc_params( pbes2_asn, enc_params);
+       result = write_pbe_enc_params(pbes2_asn, enc_params);
        if (result < 0) {
                gnutls_assert();
                goto error;
        }
 
-       result = _gnutls_x509_der_encode_and_copy( pbes2_asn, "", 
-               pkcs8_asn, "encryptionAlgorithm.parameters");
+       result = _gnutls_x509_der_encode_and_copy(pbes2_asn, "",
+                                                 pkcs8_asn,
+                                                 "encryptionAlgorithm.parameters");
        if (result < 0) {
                gnutls_assert();
                goto error;
        }
 
-       asn1_delete_structure( &pbes2_asn);
-       
+       asn1_delete_structure(&pbes2_asn);
+
        return 0;
 
-       error:
-               asn1_delete_structure( &pbes2_asn);
-               return result;
+      error:
+       asn1_delete_structure(&pbes2_asn);
+       return result;
 
 }
 
-static int encrypt_data( const gnutls_datum* plain, 
-       const struct pbe_enc_params *enc_params, 
-       gnutls_datum* key, gnutls_datum* encrypted)
+static int encrypt_data(const gnutls_datum * plain,
+                       const struct pbe_enc_params *enc_params,
+                       gnutls_datum * key, gnutls_datum * encrypted)
 {
-int result;
-int data_size;
-opaque* data=NULL;
-gnutls_datum div;
-GNUTLS_CIPHER_HANDLE ch = NULL;
-opaque pad;
-
-       data = gnutls_malloc( plain->size + 8);
+       int result;
+       int data_size;
+       opaque *data = NULL;
+       gnutls_datum div;
+       GNUTLS_CIPHER_HANDLE ch = NULL;
+       opaque pad;
+
+       data = gnutls_malloc(plain->size + 8);
        if (data == NULL) {
                gnutls_assert();
                return GNUTLS_E_MEMORY_ERROR;
        }
 
-       memcpy( data, plain->data, plain->size);
-       pad = 8 - (plain->size % 8); 
-       if ( pad == 0) pad = 8;
-       
-       memset( &data[plain->size], pad, pad);
+       memcpy(data, plain->data, plain->size);
+       pad = 8 - (plain->size % 8);
+       if (pad == 0)
+               pad = 8;
+
+       memset(&data[plain->size], pad, pad);
        data_size = plain->size + pad;
 
-       div.data = (opaque*) enc_params->iv;
+       div.data = (opaque *) enc_params->iv;
        div.size = enc_params->iv_size;
-       ch = _gnutls_cipher_init( GNUTLS_CIPHER_3DES_CBC, key, &div);
+       ch = _gnutls_cipher_init(enc_params->cipher, key, &div);
 
        if (ch == GNUTLS_CIPHER_FAILED) {
                gnutls_assert();
@@ -1165,7 +1454,7 @@ opaque pad;
                goto error;
        }
 
-       result = _gnutls_cipher_encrypt( ch, data, data_size);
+       result = _gnutls_cipher_encrypt(ch, data, data_size);
        if (result < 0) {
                gnutls_assert();
                goto error;
@@ -1174,15 +1463,118 @@ opaque pad;
        encrypted->data = data;
        encrypted->size = data_size;
 
-       _gnutls_cipher_deinit( ch);
+       _gnutls_cipher_deinit(ch);
 
        return 0;
 
-       error:
-               gnutls_free(data);
-               if (ch!=NULL)
-                       _gnutls_cipher_deinit( ch);
-               return result;
+      error:
+       gnutls_free(data);
+       if (ch != NULL)
+               _gnutls_cipher_deinit(ch);
+       return result;
 }
 
+/* Decrypts a PKCS #7 encryptedData. The output is allocated
+ * and stored in dec.
+ */
+int _gnutls_x509_decrypt_pkcs7_encrypted_data(const gnutls_datum * data,
+                                             const char *password,
+                                             gnutls_datum * dec)
+{
+       int result, len;
+       opaque enc_oid[64];
+       gnutls_datum tmp;
+       ASN1_TYPE pbes2_asn = ASN1_TYPE_EMPTY, pkcs7_asn = ASN1_TYPE_EMPTY;
+       int params_start, params_end, params_len;
+       struct pbkdf2_params kdf_params;
+       struct pbe_enc_params enc_params;
+       schema_id schema;
+
+       if ((result =
+            asn1_create_element(_gnutls_get_pkix(),
+                                "PKIX1.pkcs-7-EncryptedData",
+                                &pkcs7_asn)) != ASN1_SUCCESS) {
+               gnutls_assert();
+               result = _gnutls_asn2err(result);
+               goto error;
+       }
+
+       result =
+           asn1_der_decoding(&pkcs7_asn, data->data, data->size, NULL);
+       if (result != ASN1_SUCCESS) {
+               gnutls_assert();
+               result = _gnutls_asn2err(result);
+               goto error;
+       }
+
+       /* Check the encryption schema OID
+        */
+       len = sizeof(enc_oid);
+       result =
+           asn1_read_value(pkcs7_asn,
+                           "encryptedContentInfo.contentEncryptionAlgorithm.algorithm",
+                           enc_oid, &len);
+       if (result != ASN1_SUCCESS) {
+               gnutls_assert();
+               result = _gnutls_asn2err(result);
+               goto error;
+       }
+
+       /* we only support PBES2 
+        */
+       if ((result = check_schema(enc_oid)) < 0) {
+               gnutls_assert();
+               goto error;
+       }
+       schema = result;
+
+       /* Get the DER encoding of the parameters.
+        */
+       result =
+           asn1_der_decoding_startEnd(pkcs7_asn, data->data, data->size,
+                                      "encryptedContentInfo.contentEncryptionAlgorithm.parameters",
+                                      &params_start, &params_end);
+       if (result != ASN1_SUCCESS) {
+               gnutls_assert();
+               result = _gnutls_asn2err(result);
+               goto error;
+       }
+       params_len = params_end - params_start + 1;
+
+       result =
+           read_pkcs_schema_params(schema, password,
+                                   &data->data[params_start],
+                                   params_len, &kdf_params, &enc_params);
+       if (result < ASN1_SUCCESS) {
+               gnutls_assert();
+               result = _gnutls_asn2err(result);
+               goto error;
+       }
+
+       /* Parameters have been decoded. Now
+        * decrypt the EncryptedData.
+        */
+
+       result =
+           decrypt_data(schema, pkcs7_asn,
+                        "encryptedContentInfo.encryptedContent", password,
+                        &kdf_params, &enc_params, &tmp);
+       if (result < 0) {
+               gnutls_assert();
+               goto error;
+       }
+
+       asn1_delete_structure(&pkcs7_asn);
+
+       *dec = tmp;
+
+       return 0;
+
+      error:
+       asn1_delete_structure(&pbes2_asn);
+       asn1_delete_structure(&pkcs7_asn);
+       return result;
+}
+
+
 #endif