doFinal主要功能是结束单部分或者多部分加密或者解密操作。单部分加密或者解密适用于需要处理的报文长度较短无需分块的情况,这个时候直接使用byte[] doFinal(byte[] input)方法即可。多部分加密或者解密适用于需要处理的报文长度长度较大,需要进行分块的情况,这个时候需要调用多次update方法变体进行部分块的加解密,最后调用doFinal方法变体进行部分加解密操作的结束。举个例子,例如处理块的大小为8,实际需要加密的报文长度为23,那么需要分三块进行加密,前面2块长度为8的报文需要调用update进行部分加密,部分加密的结果可以从update的返回值获取到,最后的7长度(其实一般会填充到长度为块长度8)的报文则调用doFinal进行加密,结束整个部分加密的操作。另外,值得注意的是只要Cipher正常调用完任一个doFinal变体方法(过程中不抛出异常),那么Cipher会重置为初始化状态,可以继续使用,这个可复用的特性可以降低创建Cipher实例的性能损耗。
updateADD方法首先ADD的意思是Additional Authentication Data(额外的身份认证数据)。updateADD也有三个方法变体:
public final void updateAAD(byte[] src) public final void updateAAD(byte[] src, int offset, int len) public final void updateAAD(ByteBuffer src)它的方法变体都只依赖一个输入缓冲区,带有额外的身份认证数据,一般使用在GCM或者CCM加解密算法中。如果使用此方法,它的调用必须在Cipher的update和doFinal变体方法之前调用,其实理解起来也很简单,身份验证必须在实际的加解密操作之前进行。目前,updateADD的资料比较少,笔者在生产环境找那个也尚未实践过,所以不做展开分析。
其他方法其他方法主要是Getter方法,用于获取Cipher的相关信息。
public final Provider getProvider():获取Cipher的提供商。
public final String getAlgorithm():获取Cipher使用的算法名称。
public final int getBlockSize():分组加密中,每一组都有固定的长度,也称为块,此方法是返回块的大小(以字节为单位)。
public final int getOutputSize(int inputLen):根据给定的输入长度inputLen(以字节为单位),返回保存下一个update或doFinal操作结果所需的输出缓冲区长度(以字节为单位)。
public final byte[] getIV():返回Cipher中的初始化向量的字节数组。
public final AlgorithmParameters getParameters():返回Cipher使用的算法参数。
public final ExemptionMechanism getExemptionMechanism():返回Cipher使用的豁免(exemption)机制对象。
public static final int getMaxAllowedKeyLength(String transformation):根据所安装的JCE策略文件,返回指定转换的最大密钥长度。
public static final AlgorithmParameterSpec getMaxAllowedParameterSpec(String transformation):根据JCE策略文件,返回Cipher指定transformation下最大的AlgorithmParameterSpec对象。
Cipher的工作流程下面画一个图来详细分析一下Cipher的工作流程:
当然上图只分析了Cipher的使用过程,其实还有一个重要的步骤就是密钥的处理,但是密钥的处理和具体的算法使用是相关的,所以图中没有体现。再放一张官方描述Cipher加载的流程:
主要过程包括:
1、创建Cipher实例,这个时候会从平台中所有的提供商(Provider)中根据transformation匹配第一个可以使用的CipherSpi实例,"算法/工作模式/填充模式"必须完全匹配才能选中。
在${JAVA_HONE}/jre/lib/security中的java.security文件中可以看到默认加载的提供商。如果需要添加额外或者自实现的Provider,可以通过java.security.Security的静态方法addProvider添加。
2、通过Cipher实例的init方法初始化Cipher,主要参数是opmode和密钥。
3、根据初始化的方式和是否需要分组处理,选择合适的方法进行调用。
Cipher的使用为了方便Cipher的使用,最好先引入apache-codec依赖,这样能简化Hex、Base64等操作。
<dependency> <groupId>commons-codec</groupId> <artifactId>commons-codec</artifactId> <version>1.11</version> </dependency>大多数情况下,加密后的byte数组的中元素取值不在Unicode码点的范围内,表面上看到的就是乱码,实际上它们是有意义的,因此需要考虑把这种byte数组转换为非乱码的字符串以便传输,常见的方式有Hex(二进制转换为十六进制)、Base64等等。下面举例中没有针对异常类型进行处理统一外抛,切勿模仿,还有,所有的字符串转化为字节数组都没有指定字符编码,因此只能使用非中文的明文进行处理。
加密模式