Cipher类为加密和解密提供密码功能。它构成了Java Cryptographic Extension(JCE)框架的核心。在本章的上述内容中,只完成了密钥的处理,并未完成加密与解密的操作。这些核心操作需要通过Cipher类来实现。
// 此类为加密和解密提供密码功能
public class Cipher
extends Object
Cipher类是一个引擎类,它需要通过getInstance()工厂方法来实例化对象。
我们可以通过指定转换模式的方式获得实例化对象,方法如下所示:
// 返回实现指定转换的 Cipher对象
public static Cipher getInstance(String transformation)
也可以在制定转换模式的同时制定该转换模式的提供者,方法如下所示:
// 返回实现指定转换的 Cipher对象
public static Cipher getInstance(String transformation, Provider provider)
// 返回实现指定转换的 Cipher对象
public static Cipher getInstance(String transformation, String provider)
注意这里的参数String transformation,通过如下代码示例:
Cipher c = Cipher.getInstance("DES");
上述实例化操作是一种最为简单的实现,并没有考虑DES分组算法的工作模式和填充模式,可通过以下方式对其设定:
Cipher c = Cipher.getInstance("DES/CBC/PKCS5Padding");
参数String transformation的格式是“算法/工作模式/填充模式”,不同的算法支持不同的工作模式以及填充模式。具体内容请参见附录。
在对Cipher对象进行初始化前,我们先来认识如下常量:
// 用于将Cipher初始化为解密模式的常量
public final static int DECRYPT_MODE
// 用于将Cipher初始化为加密模式的常量
public final static int ENCRYPT_MODE
通过这两个常量来完成用于加密或是解密操作的初始化,可以使用如下这个最为简单也是最为常用的方法:
// 用密钥初始化此 Cipher对象
public final void init(int opmode, Key key)
或者使用算法参数规范或算法参数来完成初始化:
// 用密钥和一组算法参数初始化此Cipher对象
public final void init(int opmode, Key key, AlgorithmParameters params)
// 用密钥和一组算法参数初始化此Cipher对象
public final void init(int opmode, Key key, AlgorithmParameterSpec params)
以下三个方法加入了SecureRandom参数:
// 用一个密钥、一组算法参数和一个随机源初始化此Cipher对象
public final void init(int opmode, Key key, AlgorithmParameterSpec params, SecureRandom random)
// 用一个密钥、一组算法参数和一个随机源初始化此Cipher对象
public final void init(int opmode, Key key, AlgorithmParameters params, SecureRandom random)
// 用密钥和随机源初始化此Cipher对象
public final void init(int opmode, Key key, SecureRandom random)
通过以下方法可借助于证书,获取其公钥来完成加密和解密操作:
// 用取自给定证书的公钥初始化此Cipher对象
public final void init(int opmode, Certificate certificate)
// 用取自给定证书的公钥和随机源初始化此Cipher对象
public final void init(int opmode, Certificate certificate, SecureRandom random)
如果需要多次更新待加密(解密)的数据可使用如下方法。
最为常用的是通过输入给定的字节数组完成更新:
/* 继续多部分加密或解密操作(具体取决于此Cipher对象的初始化方式),以处理其他数据部分*/
public final byte[] update(byte[] input)
或者通过偏移量的方式完成更新,方法如下所示:
/* 继续多部分加密或解密操作(具体取决于此Cipher对象的初始化方式),以处理其他数据部分*/
public final byte[] update(byte[] input, int inputOffset, int inputLen)
另外一种方式就是将更新结果输出至参数中,方法如下所示:
/* 继续多部分加密或解密操作(具体取决于此Cipher对象的初始化方式),以处理其他数据部分*/
public final int update(byte[] input, int inputOffset, int inputLen, byte[] output)
/* 继续多部分加密或解密操作(具体取决于此Cipher对象的初始化方式),以处理其他数据部分*/
public final int update(byte[] input, int inputOffset, int inputLen, byte[] output, int outputOffset)
当然,我们也可以使用如下缓冲方式:
/* 继续多部分加密或解密操作(具体取决于此Cipher对象的初始化方式),以处理其他数据部分*/
public final int update(ByteBuffer input, ByteBuffer output)
完成上述数据更新后,直接执行如下方法:
// 结束多部分加密或解密操作(具体取决于此Cipher对象的初始化方式)
public final byte[] doFinal()
如果,加密(解密)操作不需要多次更新数据可以直接执行如下方法:
// 按单部分操作加密或解密数据,或者结束一个多部分操作
public final byte[] doFinal(byte[] input)
或按以下偏移量的方式完成操作:
// 按单部分操作加密或解密数据,或者结束一个多部分操作
public final byte[] doFinal(byte[] input, int inputOffset, int inputLen)
以下方式将操作后的结果存于给定的参数中,与上述方法大同小异:
// 结束多部分加密或解密操作(具体取决于此 Cipher 的初始化方式)
public final int doFinal(byte[] output, int outputOffset)
与上述方法不同的是,以下方法可用于多部分操作,并将操作结果存于给定参数中:
// 按单部分操作加密或解密数据,或者结束一个多部分操作
public final int doFinal(byte[] input, int inputOffset, int inputLen, byte[] output)
// 按单部分操作加密或解密数据,或者结束一个多部分操作
public final int doFinal(byte[] input, int inputOffset, int inputLen, byte[] output, int outputOffset)
以下方法提供了一种基于缓冲的处理方式:
// 按单部分操作加密或解密数据,或者结束一个多部分操作
public final int doFinal(ByteBuffer input, ByteBuffer output)
除了完成数据的加密与解密,Cipher类还提供了对密钥的包装与解包。
我们先来了解一下与密钥包装有关的常量:
// 用于将Cipher对象初始化为密钥包装模式的常量
public final static int WRAP_MODE
这一常量需要在进行Cipher对象初始化时使用,给出如下示例代码:
cipher.init(Cipher.WRAP_MODE, secretKey); //初始化
在此之后我们就可以执行包装操作,可使用如下方法:
// 包装密钥
public final byte[] wrap(Key key)
解包操作需要如下常量执行初始化:
// 用于将Cipher初始化为密钥解包模式的常量
public final static int UNWRAP_MODE
这个常量同样需要在初始化中执行,给出如下示例代码:
cipher.init(Cipher.UNWRAP _MODE, secretKey); //初始化
在此之后才能执行解包操作。
我们先来看一下解包方法:
// 解包一个以前包装的密钥
public final Key unwrap(byte[] wrappedKey, String wrappedKeyAlgorithm, int wrappedKeyType)
上述方法中的参数int wrappedKeyType需要使用如下常量:
// 用于表示要解包的密钥为“私钥”的常量
public final static int PRIVATE_KEY
// 用于表示要解包的密钥为“公钥”的常量
public final static int PUBLIC_KEY
// 用于表示要解包的密钥为“秘密密钥”的常量
public final static int SECRET_KEY
在执行包装操作时使用的是私钥就使用私钥常量,依此对应。
如果读者对第2章中有关分组加密工作模式的内容还有印象,应该记得文中曾提到过初始化向量。我们可以通过如下方法获得:
// 返回新缓冲区中的初始化向量 (IV)
public final byte[] getIV()
通常,我们有必要通过如下方法来获悉当前转换模式所支持的密钥长度,方法如下所示:
// 根据所安装的 JCE 仲裁策略文件,返回指定转换的最大密钥长度
public final static int getMaxAllowedKeyLength(String transformation)
分组加密中,每一组都有固定的长度,也称为块,以下方法可以获得相应的块大小:
// 返回块的大小(以字节为单位)
public final int getBlockSize()
以下方法获得输出缓冲区字节长度:
/* 根据给定的输入长度 inputLen(以字节为单位),返回保存下一个 update 或 doFinal 操作结果所需的输出缓冲区长度(以字节为单位)*/
public final int getOutputSize(int inputLen)
我们也可以通过如下方法获得该Cipher对象的算法参数相关信息:
/* 根据仲裁策略文件,返回包含最大 Cipher 参数值的 AlgorithmParameterSpec 对象*/
public final static AlgorithmParameterSpec getMaxAllowedParameterSpec(String transformation)
// 返回此 Cipher 使用的参数
public final AlgorithmParameters getParameters()
此外,Cipher类还提供以下方法:
// 返回此 Cipher 使用的豁免 (exemption) 机制对象
public final ExemptionMechanism getExemptionMechanism()
Cipher类作为一个引擎类,同样提供如下方法:
// 返回此 Cipher 对象的提供者
public final Provider getProvider()
// 返回此 Cipher 对象的算法名称
public final String getAlgorithm()
NullCipher和Cipher是什么关系?
我们会看到在API文档中有一个NullCipher类,它是Cipher的子类,用来验证程序的有效性,并不提供具体的加密和解密实现。在验证程序的时候将会用到它。
可通过如下代码完成密钥的包装和解包操作:
// 实例化KeyGenerator对象,并指定DES算法
KeyGenerator keyGenerator = KeyGenerator.getInstance("DES");
// 生成SecretKey对象
SecretKey secretKey = keyGenerator.generateKey();
// 实例化Cipher对象
Cipher cipher = Cipher.getInstance("DES");
接下来执行包装操作:
// 初始化Cipher对象,用于包装
cipher.init(Cipher.WRAP_MODE, secretKey);
// 包装秘密密钥
byte[] k = cipher.wrap(Key key);
得到字节数组k后,可以将其传递给需要解包的一方。
省去上述实例化操作用以下代码示例:
// 初始化Cipher对象,用于解包
cipher.init(Cipher.UNWRAP_MODE, secretKey);
// 解包秘密密钥
Key key = cipher.unwrap(k, "DES", Cipher.SECRET_KEY);
如果要做加密操作可按参考如下代码:
// 初始化Cipher对象,用于加密操作
cipher.init(Cipher.ENCRYPT_MODE, secretKey);
// 加密
byte[] input = cipher.doFinal("DES DATA".getBytes());
解密操作与之相对应:
// 初始化Cipher对象,用于解密操作
cipher.init(Cipher.DECRYPT_MODE, secretKey);
// 解密
byte[] output = cipher.doFinal(input);