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

时间:2021-07-10 17:58:37

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