int decode_pkcs8_key(const gnutls_datum_t * raw_key,
		     const char *password,
		     gnutls_x509_privkey_t pkey, ASN1_TYPE * out)
{
    int result, len;
    char enc_oid[64];
    gnutls_datum_t tmp;
    ASN1_TYPE pbes2_asn = ASN1_TYPE_EMPTY, pkcs8_asn = ASN1_TYPE_EMPTY;
    ASN1_TYPE ret_asn;
    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) {
	gnutls_assert();
	result = _gnutls_asn2err(result);
	goto error;
    }

    result =
	asn1_der_decoding(&pkcs8_asn, raw_key->data, raw_key->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(pkcs8_asn, "encryptionAlgorithm.algorithm",
			enc_oid, &len);
    if (result != ASN1_SUCCESS) {
	gnutls_assert();
	goto error;
    }

    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(pkcs8_asn, raw_key->data,
				   raw_key->size,
				   "encryptionAlgorithm.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,
				&raw_key->data[params_start],
				params_len, &kdf_params, &enc_params);


    /* Parameters have been decoded. Now
     * decrypt the EncryptedData.
     */
    result =
	decrypt_data(schema, pkcs8_asn, "encryptedData", password,
		     &kdf_params, &enc_params, &tmp);
    if (result < 0) {
	gnutls_assert();
	goto error;
    }

    asn1_delete_structure(&pkcs8_asn);

    result = decode_private_key_info(&tmp, pkey, &ret_asn);
    _gnutls_free_datum(&tmp);

    if (result < 0) {
	gnutls_assert();
	goto error;
    }

    *out = ret_asn;

    return 0;

  error:
    asn1_delete_structure(&pbes2_asn);
    asn1_delete_structure(&pkcs8_asn);
    return result;
}
int decode_pkcs8_key(const gnutls_datum_t * raw_key,
		     const char *password,
		     gnutls_x509_privkey_t pkey, ASN1_TYPE * out)
{
    int result, len;
    char enc_oid[64];
    gnutls_datum_t tmp;
    ASN1_TYPE pbes2_asn = ASN1_TYPE_EMPTY, pkcs8_asn = ASN1_TYPE_EMPTY;
    ASN1_TYPE ret_asn;
    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) {
	gnutls_assert();
	result = _gnutls_asn2err(result);
	goto error;
    }

    result =
	asn1_der_decoding(&pkcs8_asn, raw_key->data, raw_key->size, NULL);
    if (result != ASN1_SUCCESS) {
	gnutls_assert();
	result = _gnutls_asn2err(result);
	goto error;
    }

     
    len = sizeof(enc_oid);
    result =
	asn1_read_value(pkcs8_asn, "encryptionAlgorithm.algorithm",
			enc_oid, &len);
    if (result != ASN1_SUCCESS) {
	gnutls_assert();
	goto error;
    }

    if ((result = check_schema(enc_oid)) < 0) {
	gnutls_assert();
	goto error;
    }

    schema = result;

     
    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();
	result = _gnutls_asn2err(result);
	goto error;
    }
    params_len = params_end - params_start + 1;

    result =
	read_pkcs_schema_params(schema, password,
				&raw_key->data[params_start],
				params_len, &kdf_params, &enc_params);


     
    result =
	decrypt_data(schema, pkcs8_asn, "encryptedData", password,
		     &kdf_params, &enc_params, &tmp);
    if (result < 0) {
	gnutls_assert();
	goto error;
    }

    asn1_delete_structure(&pkcs8_asn);

    result = decode_private_key_info(&tmp, pkey, &ret_asn);
    _gnutls_free_datum(&tmp);

    if (result < 0) {
	gnutls_assert();
	goto error;
    }

    *out = ret_asn;

    return 0;

  error:
    asn1_delete_structure(&pbes2_asn);
    asn1_delete_structure(&pkcs8_asn);
    return result;
}