13.4 加密与解密、报文摘要和数字签名

构建安全的应用离不开加密和解密操作。当一个程序中的数据被保存到存储介质上或通过网络方式传输时,为了保证数据的安全性,需要对数据进行一定的处理。如果不希望数据被恶意的第三方所窃取,那么可以对数据进行加密。经过加密处理之后,即便数据被窃取,窃取者也无法获取数据的真实内容。如果希望确保数据没有被第三方篡改,那么可以从数据中创建报文摘要或添加消息验证码。Java密码框架中提供了加密、解密、报文摘要和数字签名等相关的API。

13.4.1 Java密码框架

Java密码框架采用了常见的服务提供者架构,以提供所需的可扩展性和互操作性。Java密码框架的设计原则是实现的独立性和互操作性,以及算法的独立性和可扩展性。密码框架可以看成一些服务的集合,加密、解密、报文摘要和数字签名等都是它所提供的服务。每个服务可能有不同的实现算法,比如报文摘要可以使用MD5算法或SHA-1算法。程序本身作为服务的消费者,并不需要了解服务本身的实现细节,只需要根据程序的需求,选择合适的服务和算法即可。服务的具体实现由Java平台或第三方提供。Java密码框架所使用的服务提供者架构使添加新的服务和算法实现变得很容易。Java标准库为不同的服务提供了各自抽象的接口。服务的不同实现之间可以互操作。

Java平台或第三方都提供了很多密码框架中的服务的实现。一个服务提供者通常会实现多个服务,并为每个服务提供多种不同的算法实现。每个服务提供者都继承自java.security.Provider类。Provider类的对象有自己的名称及它所实现的服务和算法的列表。程序如果要使用第三方的服务提供者所提供的实现,需要先把相应的jar包放在程序的类路径或JDK的标准扩展目录下,再通过修改配置文件或调用java.security.Security类的addProvider方法来进行添加。通过Security类的getProviders方法可以得到包含当前所有可用的Provider类的对象的数组。

代码清单13-15给出了Provider类的使用示例。通过getServices方法可以得到Provider类的对象表示的服务提供者所能提供的服务的列表。在表示服务的Provider.Service类的对象中,可以使用getAlgorithm、getClassName和getType方法分别获取服务所实现的算法、对应的Java类名和服务的类别。

代码清单13-15 Provider类的使用示例


Provider[]providers=Security.getProviders();

for(Provider provider:providers){

Set<Provider.Service>services=provider.getServices();

for(Provider.Service service:services){

System.out.println(service.getAlgorithm()+"<==>"+service.getClassName());

}

}


当系统中存在一个服务的多个实现时,如果服务的消费者没有显式指定该服务的提供者的名称,则Java平台会根据服务提供者的优先级来选择最合适的实现。服务的消费者在使用一个服务时只需要确定服务的类别、所用的算法及可选的提供者的名称。对于不同类别的服务,Java标准库提供了对应的Java类作为该服务的抽象表示。以报文摘要服务为例来说,java.security.MessageDigest类是该服务的抽象表示。这些抽象表示类一般都提供一个getInstance方法根据算法名称和提供者的名称来创建出具体的实现对象。对于每个服务,Java标准库还提供了对应的服务提供者接口,是一些抽象的Java类(SPI类)。这些SPI类的命名规则是在服务的抽象表示类名之后加上“Spi”,如MessageDigest类对应的SPI类是java.security.MessageDigestSpi。服务的具体实现者需要继承自对应的SPI类,并实现自己的逻辑。这些SPI类的存在,使得在不同服务提供者的实现之间可以进行互操作。