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

在使用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;
}


  • 8
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值