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);