EVP_DecryptFinal_ex调用返回失败,解密数据错误的解决方法

本文探讨了解决OpenSSL中AES加密解密时遇到的问题,特别是针对数据长度为16的整数倍时出现的解密异常。通过调整EVP_DecryptFinal_ex函数的调用逻辑,确保了解密过程的正确性和完整性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在使用openssl进行数据加解密时,解密数据时偶尔会出现问题,即当数据长度为16的整数倍时会出现解密数据部分不正确的情况。此情况下EVP_DecryptFinal_ex函数调用失败。查阅资料如下:

【EVP_EncryptFinal_ex】

    该函数处理最后(Final)的一段数据。在函数在padding功能打开的时候(缺省)才有效,这时候,它将剩余的最后的所有数据进行加密处理。该算法使用标志的块padding方式(AKA PKCS padding)。加密后的数据写入到参数out里面,参数out的长度至少应该能够一个加密块。写入的数据长度信息输入到outl参数里面。该函数调用后,表示所有数据都加密完了,不应该再调用EVP_EncryptUpdate函数。如果没有设置padding功能,那么本函数不会加密任何数据,如果还有剩余的数据,那么就会返回错误信息,也就是说,这时候数据总长度不是块长度的整数倍。操作成功返回1,否则返回0。

    PKCS padding标准是这样定义的,在被加密的数据后面加上n个值为n的字节,使得加密后的数据长度为加密块长度的整数倍。无论在什么情况下,都是要加上padding的,也就是说,如果被加密的数据已经是块长度的整数倍,那么这时候n就应该等于块长度。比如,如果块长度是9,要加密的数据长度是11,那么5个值为5的字节就应该增加在数据的后面。

    【EVP_DecryptInit_ex, EVP_DecryptUpdate和EVP_DecryptFinal_ex】

    这三个函数是上面三个函数相应的解密函数。这些函数的参数要求基本上都跟上面相应的加密函数相同。如果padding功能打开了,EVP_DecryptFinal会检测最后一段数据的格式,如果格式不正确,该函数会返回错误代码。此外,如果打开了padding功能,EVP_DecryptUpdate函数的参数out的长度应该至少为(inl+cipher_block_size)字节;但是,如果加密块的长度为1,则其长度为inl字节就足够了。三个函数都是操作成功返回1,否则返回0。

    需要注意的是,虽然在padding功能开启的情况下,解密操作提供了错误检测功能,但是该功能并不能检测输入的数据或密钥是否正确,所以即便一个随机的数据块也可能无错的完成该函数的调用。如果padding功能关闭了,那么当解密数据长度是块长度的整数倍时,操作总是返回成功的结果。

将代码修改如下,即当解密长度为16的整数倍时,执行函数EVP_DecryptFinal_ex,则解密成功。
int AesEncryptBuf (  IN unsigned char * szIn,
				     IN int inLen,
					 OUT unsigned char * szOut ,
					 IN int outLen,
					 IN unsigned char * key,
					 IN int iKeyLen,
					 IN int iType)
{
	unsigned char ukey[EVP_MAX_KEY_LENGTH];
	unsigned char iv[EVP_MAX_IV_LENGTH];
	unsigned char in[N];
	int outl = 0;   //单次update输出数据大小
	int outl_total = 0;

	int isSuccess;

	//evp加密上下文环境
	EVP_CIPHER_CTX ctx;   
	const	EVP_CIPHER *cipher;

	//选择算法
	if(iType == 128)
	{
		cipher = EVP_aes_128_ecb();
	}
	else if(iType == 256)
	{
		cipher = EVP_aes_256_ecb();
	}
	else
	{
		printf("iType should be 128 or 256.");
		return 0;
	}
	//生成ukey和iv
	int len = sizeof(key);
	EVP_BytesToKey(cipher, EVP_md5(), NULL, (const unsigned char *)key, len-1/*iKeyLen*/, 1, ukey, iv);

	//初始化ctx,加密算法初始化
	EVP_CIPHER_CTX_init(&ctx);
	isSuccess = EVP_EncryptInit_ex(&ctx,cipher,NULL,ukey,iv);
	if(!isSuccess)
	{
		printf("EVP_EncryptInit_ex() failed");
		EVP_CIPHER_CTX_cleanup(&ctx);

		return 0;
	}

	//加密数据
	while(inLen > N)
	{
		memcpy(in, szIn, N);
		inLen -= N;
		szIn += N;

		isSuccess = EVP_EncryptUpdate(&ctx, szOut + outl_total, &outl, in, N);
		if(!isSuccess)
		{
			printf("EVP_EncryptUpdate() failed");
			EVP_CIPHER_CTX_cleanup(&ctx);

			return 0;
		}
		outl_total += outl;
	}

	if(inLen > 0)
	{
		memcpy(in, szIn, inLen);
		isSuccess = EVP_EncryptUpdate(&ctx, szOut + outl_total, &outl, in, inLen);
		outl_total += outl;
	}	

	isSuccess = EVP_EncryptFinal_ex(&ctx,szOut + outl_total,&outl);
	if(!isSuccess)
	{
		printf("EVP_EncryptFinal_ex() failed");
		EVP_CIPHER_CTX_cleanup(&ctx);

		return 0;
	}
	outl_total += outl;
		
	printf("AesEncryptBuf加密成功\n");
	EVP_CIPHER_CTX_cleanup(&ctx);
	return 1;
}


int AesDecryptBuf (  IN unsigned char * szIn,
					 IN int inLen,
					 IN unsigned char * szOut ,
					 IN int outLen,
					 IN unsigned char * key,
					 IN int iKeyLen,
					 IN int iType)
{
	unsigned char ukey[EVP_MAX_KEY_LENGTH];
	unsigned char iv[EVP_MAX_IV_LENGTH];
	unsigned char in[N];
	//int inl;   //输入数据大小
	//unsigned char out[N];
	int outl = 0;   //输出数据大小
	int outl_total = 0;
	int isSuccess;

	EVP_CIPHER_CTX ctx;   //evp加密上下文环境
	const EVP_CIPHER *cipher;

	//选择算法
	if(iType == 128)
	{
		cipher = EVP_aes_128_ecb();
	}
	else if(iType == 256)
	{
		cipher = EVP_aes_256_ecb();
	}
	else
	{
		printf("iType should be 128 or 256.");
		return 0;
	}
	//生成ukey和iv
	int len = sizeof(key);
	EVP_BytesToKey(cipher,EVP_md5(),NULL,(const unsigned char *)key, len-1/*iKeyLen*/, 1, ukey, iv);

	//初始化ctx,加密算法初始化
	EVP_CIPHER_CTX_init(&ctx);
	isSuccess = EVP_DecryptInit_ex(&ctx,cipher,NULL,ukey,iv);
	if(!isSuccess)
	{
		printf("EVP_DecryptInit_ex() failed");
		EVP_CIPHER_CTX_cleanup(&ctx);
		return 0;
	}

	//解密数据
	while(inLen > N)
	{
		memcpy(in, szIn, N);
		inLen -= N;
		szIn += N;

		isSuccess = EVP_DecryptUpdate(&ctx, szOut + outl_total, &outl, in, N);
		if(!isSuccess)
		{
			printf("EVP_DecryptUpdate() failed");
			EVP_CIPHER_CTX_cleanup(&ctx);
			return 0;
		}
		outl_total += outl;
	}

	if(inLen > 0)
	{
		memcpy(in, szIn, inLen);
		isSuccess = EVP_DecryptUpdate(&ctx, szOut + outl_total, &outl, in, inLen);
		outl_total += outl;
	}	
	
	//如果解密数据是分组长度16的整数倍,EVP_DecryptFinal_ex会调用失败而且解密数据不正确
	//因此当解密数据为16的整数倍时,不执行EVP_DecryptFinal_ex,解密结果正确
	if(inLen % 16 != 0)
	{
		isSuccess = EVP_DecryptFinal_ex(&ctx, szOut + outl_total, &outl);
		if(!isSuccess)
		{
			printf("EVP_DecryptFinal_ex() failed\n");
			EVP_CIPHER_CTX_cleanup(&ctx);
			return 0;
		}
		outl_total += outl;
	}
	

	printf("AesDecryptBuf解密成功\n");
	EVP_CIPHER_CTX_cleanup(&ctx);
	return 1;
}


// AES解密 QByteArray KeyUtils::aesDecrypt(const QByteArray& data, const QByteArray& key) { if (key.size() != AES_BLOCK_SIZE || data.size() <= AES_BLOCK_SIZE) { return QByteArray(); } EVP_CIPHER_CTX* ctx = EVP_CIPHER_CTX_new(); if (!ctx) return QByteArray(); // 提取IV QByteArray iv = data.left(AES_BLOCK_SIZE); QByteArray ciphertext = data.mid(AES_BLOCK_SIZE); if (EVP_DecryptInit_ex(ctx, EVP_aes_256_cbc(), nullptr, reinterpret_cast<const unsigned char*>(key.constData()), reinterpret_cast<const unsigned char*>(iv.constData())) != 1) { EVP_CIPHER_CTX_free(ctx); return QByteArray(); } // 准备输出缓冲区 int out_len = ciphertext.size() + AES_BLOCK_SIZE; unsigned char* out = new unsigned char[out_len]; int len1, len2; if (EVP_DecryptUpdate(ctx, out, &len1, reinterpret_cast<const unsigned char*>(ciphertext.constData()), ciphertext.size()) != 1) { delete[] out; EVP_CIPHER_CTX_free(ctx); return QByteArray(); } //输出 { // 验证密钥(key)的一致性 int nSize = key.size(); //密钥长度 QByteArray arr = key.toHex(); //密钥内容 //验证 IV(初始化向量)的正确性 int nIV = iv.size(); QByteArray arrIV = iv.toHex(); //检查密文(ciphertext)的完整性 int nCiphertext = ciphertext.size(); //密文长度 QByteArray arrCiphertext = ciphertext.left(16).toHex(); int n = EVP_DecryptFinal_ex(ctx, out + len1, &len2); qDebug() << "..."; } if (EVP_DecryptFinal_ex(ctx, out + len1, &len2) != 1) { // 打印错误信息 unsigned long err; while ((err = ERR_get_error()) != 0) { char errMsg[256]; ERR_error_string_n(err, errMsg, sizeof(errMsg)); qDebug() << "OpenSSL 错误:" << errMsg; // 关键:根据错误信息定位问题 } delete[] out; EVP_CIPHER_CTX_free(ctx); return QByteArray(); } int total_len = len1 + len2; QByteArray result(reinterpret_cast<char*>(out), total_len); delete[] out; EVP_CIPHER_CTX_free(ctx); return result; }上面内容在QT Lib静态库中执行EVP_DecryptFinal_ex值为0,errmsg错误:"error:06065064:digital envelope routines:EVP_DecryptFinal_ex:bad decrypt. 但是上面同样的代码,在单独的exe执行值为1。其中输出部分如arr,arrIV,arrCiphertext等在lib静态库和exe中值输出是完全一样的。分析原因
最新发布
08-01
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值