在使用openssl进行数据加解密时,解密数据时偶尔会出现问题,即当数据长度为16的整数倍时会出现解密数据部分不正确的情况。此情况下EVP_DecryptFinal_ex函数调用失败。查阅资料如下:
将代码修改如下,即当解密长度为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功能关闭了,那么当解密数据长度是块长度的整数倍时,操作总是返回成功的结果。
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; }