ing)。加密后的数据写入到参数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功能关闭了,那么当解密数据长度是块长度的整数倍时,操作总是返回成功的结果。
前面我们说过,openssl的填充padding方式不能自定义,之后采用默认的在尾端加字符'\0',但是EVP会默认打开Padding,且使用的Padding方式为PKCS padding,所以只要java使用对应的填充方式,理论上加解密的结果是一样的。知道了这些函数,如何使用呢?上个代码(整理后的):
void encrypt(unsigned char* in, int inl, unsigned char *out, int* len, unsigned char * key){
? ? unsigned char iv[8];
? ? EVP_CIPHER_CTX ctx;
? ? //此init做的仅是将ctx内存 memset为0?
? ? EVP_CIPHER_CTX_init(&ctx);
?
? ? //cipher? = EVP_aes_128_ecb();?
? ? //原型为int EVP_EncryptInit_ex(EVP_CIPHER_CTX *ctx,const EVP_CIPHER *cipher, ENGINE *impl, const unsigned char *key, const unsigned char *iv)?
? ? //另外对于ecb电子密码本模式来说,各分组独立加解密,前后没有关系,也用不着iv?
? ? EVP_EncryptInit_ex(&ctx, EVP_aes_128_ecb(), NULL, key, iv);?
?
? ? *len = 0;
? ? int outl = 0;
? ? //这个EVP_EncryptUpdate的实现实际就是将in按照inl的长度去加密,实现会取得该cipher的块大小(对aes_128来说是16字节)并将block-size的整数倍去加密。
? ? //如果输入为50字节,则此处仅加密48字节,outl也为48字节。输入in中的最后两字节拷贝到ctx->buf缓存起来。?
? ? //对于inl为block_size整数倍的情形,且ctx->buf并没有以前遗留的数据时则直接加解密操作,省去很多后续工作。?
? ? EVP_EncryptUpdate(&ctx, out+*len, &outl, in+*len, inl);
? ? *len+=outl;
? ? //余下最后n字节。此处进行处理。
? ? //如果不支持pading,且还有数据的话就出错,否则,将block_size-待处理字节数个数个字节设置为此个数的值,如block_size=16,数据长度为4,则将后面的12字节设置为16-4=12,补齐为一个分组后加密
? ? //对于前面为整分组时,如输入数据为16字节,最后再调用此Final时,不过是对16个0进行加密,此密文不用即可,也根本用不着调一下这Final。
? ? int test = inl>>4;
? ? if(inl != test<<4){
? ? ? ? EVP_EncryptFinal_ex(&ctx,out+*len,&outl);?
? ? ? ? *len+=outl;
? ? }
? ? EVP_CIPHER_CTX_cleanup(&ctx);
}