int PKCS5_v2_PBKDF2_keyivgen(EVP_CIPHER_CTX *ctx, const char *pass, int passlen,
                         ASN1_TYPE *param,
			 const EVP_CIPHER *c, const EVP_MD *md, int en_de)
{
	unsigned char *salt, key[EVP_MAX_KEY_LENGTH];
	const unsigned char *pbuf;
	int saltlen, iter, plen;
	int rv = 0;
	unsigned int keylen;
	int prf_nid, hmac_md_nid;
	PBKDF2PARAM *kdf = NULL;
	const EVP_MD *prfmd;

	if (EVP_CIPHER_CTX_cipher(ctx) == NULL)
		{
		EVPerr(EVP_F_PKCS5_V2_PBKDF2_KEYIVGEN,EVP_R_NO_CIPHER_SET);
		goto err;
		}
	keylen = EVP_CIPHER_CTX_key_length(ctx);
	OPENSSL_assert(keylen <= sizeof key);

	/* Decode parameter */

	if(!param || (param->type != V_ASN1_SEQUENCE))
		{
		EVPerr(EVP_F_PKCS5_V2_PBKDF2_KEYIVGEN,EVP_R_DECODE_ERROR);
		goto err;
		}

	pbuf = param->value.sequence->data;
	plen = param->value.sequence->length;

	if(!(kdf = d2i_PBKDF2PARAM(NULL, &pbuf, plen)) ) {
		EVPerr(EVP_F_PKCS5_V2_PBKDF2_KEYIVGEN,EVP_R_DECODE_ERROR);
		goto err;
	}

	keylen = EVP_CIPHER_CTX_key_length(ctx);

	/* Now check the parameters of the kdf */

	if(kdf->keylength && (ASN1_INTEGER_get(kdf->keylength) != (int)keylen)){
		EVPerr(EVP_F_PKCS5_V2_PBKDF2_KEYIVGEN,
						EVP_R_UNSUPPORTED_KEYLENGTH);
		goto err;
	}

	if (kdf->prf)
		prf_nid = OBJ_obj2nid(kdf->prf->algorithm);
	else
		prf_nid = NID_hmacWithSHA1;

	if (!EVP_PBE_find(EVP_PBE_TYPE_PRF, prf_nid, NULL, &hmac_md_nid, 0))
		{
		EVPerr(EVP_F_PKCS5_V2_PBKDF2_KEYIVGEN, EVP_R_UNSUPPORTED_PRF);
		goto err;
		}

	prfmd = EVP_get_digestbynid(hmac_md_nid);
	if (prfmd == NULL)
		{
		EVPerr(EVP_F_PKCS5_V2_PBKDF2_KEYIVGEN, EVP_R_UNSUPPORTED_PRF);
		goto err;
		}

	if(kdf->salt->type != V_ASN1_OCTET_STRING) {
		EVPerr(EVP_F_PKCS5_V2_PBKDF2_KEYIVGEN,
						EVP_R_UNSUPPORTED_SALT_TYPE);
		goto err;
	}

	/* it seems that its all OK */
	salt = kdf->salt->value.octet_string->data;
	saltlen = kdf->salt->value.octet_string->length;
	iter = ASN1_INTEGER_get(kdf->iter);
	if(!PKCS5_PBKDF2_HMAC(pass, passlen, salt, saltlen, iter, prfmd,
						   keylen, key))
		goto err;
	rv = EVP_CipherInit_ex(ctx, NULL, NULL, key, NULL, en_de);
	err:
	OPENSSL_cleanse(key, keylen);
	PBKDF2PARAM_free(kdf);
	return rv;
}