3.3.5 Cipher
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类,它用来验证程序的有效性,并不提供具体的加密和解密实现。在验证程序的时候将会用到它。
❑实现示例
可通过如下代码完成密钥的包装和解包操作:
//实例化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);