JSSE知识概览
1. 什么是JSSE
JSSE(Java Security Socket Extension,Java安全套接字扩展)是Sun为了解决在Internet上的安全通讯而推出的解决方案。它实现了SSL和TSL(传输层安全)协议。在JSSE中包含了数据加密,服务器验证,消息完整性和客户端验证等技术。通过使用JSSE,开发人员可以在客户机和服务器之间通过TCP/IP协议安全地传输数据。
2.SSL和TLS
安全套接层协议(SSL,Security Socket Layer)是网景(Netscape)公司提出的基于WEB应用的安全协议,它包括:服务器认证、客户认证(可选)、SSL链路上的数据完整性和SSL链路上的数据保密性。SSL (Secure socket Layer)安全套接层协议主要是使用公开密钥体制和X.509数字证书技术保护信息传输的机密性和完整性,它不能保证信息的不可抵赖性,主要适用于点对点之间的信息传输,常用Web Server方式。
服务器认证为客户端需要验证服务器端的身份。客户端请求服务器的连接,这个请求里包含了自己可以实现的算法列表和其它一些需要的消息,然后服务器回应这个请求,确定了这次通信所需的算法,然后发给客户端自己的证书(包含自己的身份信息和公钥)。客户端会确认服务器端发送过来的证书是不是自己所信任的证书,然后再进行通信。
客户认证为服务器端需要确认用户的身份。服务器端接受客户端发送过来的证书,然后看客户端的证书是不是可信的(要可信任的证书库中查找客户是不是由可信任的机构签发的),再决定是否响应客户端的正常连接请求。
SSL链路上的数据完整性和SSL链路上的数据保密性为发送方对传输在SSL层上的数据进行加密,然后接收方利用发送方的公钥对加密了的数据进行解密。这样确保了传输的数据的保密而不能被第三方轻易的猜解出内容。
IETF(www.ietf.org)将SSL作了标准化,即RFC2246,并将其称为TLS(Transport Layer Security),从技术上讲,TLS1.0与SSL3.0的差别非常微小。
3、 证书和证书库
证书是数字证书或电子证书的简称,它符合X.509标准,是网上实体身份的证明。证书是由具备权威性、可信任性和公正性的第三方机构(CA)签发的,因此,它是权威性的电子文档。
证书库是CA颁发证书和撤消证书的集中存放地,它像网上的“白页”一样,是网上的公共信息库,可供公众进行开放式查询。一般来说,查询的目的有两个:其一是想得到与之通信实体的公钥;其二是要验证通信对方的证书是否已进入 “黑名单”。
JDK中使用的 SSL/TLS 证书是 X.509 证书
X.509的最初版本公布于1988年。X.509证书由用户公共密钥和用户标识符组成。此外还包括版本号、证书序列号、CA标识符、签名算法标识、签发者名称、证书有效期等信息。这一标准的最新版本是X.509 v3,它定义了包含扩展信息的数字证书。该版数字证书提供了一个扩展信息字段,用来提供更多的灵活性及特殊应用环境下所需的信息传送。
4.常用公钥算法
下面是三种最常用的公钥算法:
RSA-适用于数字签名和密钥交换。Rivest-Shamir-Adleman (RSA) 加密算法是目前应用最广泛的公钥加密算法,特别适用于通过 Internet 传送的数据。这种算法以它的三位发明者的名字命名:Ron Rivest、Adi Shamir 和 Leonard Adleman。RSA 算法的安全性基于分解大数字时的困难(就计算机处理能力和处理时间而言)。在常用的公钥算法中,RSA 与众不同,它能够进行数字签名和密钥交换运算。
DSA-仅适用于数字签名。数字签名算法 (Digital Signature Algorithm, DSA) 由美国国家安全署 (United States National Security Agency, NSA) 发明,已经由美国国家标准与技术协会 (National Institute of Standards and Technology, NIST) 收录到联邦信息处理标准 (Federal Information Processing Standard, FIPS) 之中,作为数字签名的标准。DSA 算法的安全性源自计算离散算法的困难。这种算法仅用于数字签名运算(不适用于数据加密)。
Diffie-Hellman-仅适用于密钥交换。 Diffie-Hellman 是发明的第一个公钥算法,以其发明者 Whitfield Diffie 和 Martin Hellman 的名字命名。Diffie-Hellman 算法的安全性源自在一个有限字段中计算离散算法的困难。Diffie-Hellman 算法仅用于密钥交换。
5. 授权认证的前期准备步骤
在一个客户访问一个需要客户认证的服务器时,需要把此用户的证书导入到服务器受信任的证书文件库中。下面介绍进行客户认证需要做的前期准备工作。首先,该客户必需有一张证书,证书一般是由权威机构颁发,即(CA 系统),这里自己为自己颁发一张证书。我们先要为自己生成生成一个密钥,并保存在密钥库中,然后从密钥库中导出证书。在服务器端,也必须有一个密钥库,在进行通信行,服务器端用自己的私钥来对发送的消息进行加密。
例:
1.生成客户密钥到密钥库。如生成一个密钥到密钥库clientKeys,该密钥库中包含了用户Alice的密钥,完成了对Alice的授权。命令如下:
keytool -genkey -alias alice -keystore clientKeys
在完成后可以键入下面的命令来检测是否已经正确完成了授权:
keytool -list -v -keystore clientKeys
2.生成服务器端的密钥到密钥库,这里密钥库取名为serverKeys,该密钥库包含服务器Server的密钥
keytool -genkey -alias server -keystore serverKeys
3.生成证书文件:如从密钥库clientKeys生成Alice的证书文件alice.cer
keytool -export -alias alice -keystore clientKeys -file alice.cer
用户有了自己的证书文件后,服务器要取得对该用户的信任,必须把该用户的证书文件(或是为该用户颁发证书文件的上一级机构)导入到服务器的受信任的证书库中:
4.将证书文件导入到服务器端受信任的证书库中。将alice.cer导入到服务器信任的证书库serverTruest文件中,serverTruest文件即为信任的证书列表文件(受信任的证书库)。
命令为:keytool -import -alias alice -keystore serverTrust -file alice.cer
同理,客户端要进行服务器验证时。需要把服务器的证文件导入到客户端受信任的证书文件库中(用户用IE访问时,需把用户的证书导入IE)。
6.编程实现对客户端的认证和安全通信
我认为实现对客户端的认证,然后与客户端进行安全加密的通信,可以参考如下8个步骤。以下结合一个完整的例子对各步骤进行说明(附HttpServer.java),程序中采用上面产生的证书和信任的证书库。
1.初始化密钥库,指明用来加密的密钥库存放位置并得到访问密钥库的权限。
KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
KeyStore ks=KeyStore.getInstance("jks");
ks.load(new FileInputStream(keyStore), keyStorePassword.toCharArray());
// 使用获得的KeyStore初始化KeyManagerFactory对象
kmf.init(ks, keyPassWord.toCharArray());
2.初始化可信任的证书库。指明可信任的证书库存放位置并得到访问证书库的权限
TrustManagerFactory tmFact=TrustManagerFactory.getInstance("SunX509");
KeyStore ks=KeyStore.getInstance("jks");
ks.load(new FileInputStream(trustStore), trustStorePassword.toCharArray());
// 使用获得的KeyStore初始化TrustManagerFactory对象
tmFact.init(ks);
3.创建SSLContext的对象
SSLContext sslcontext = SSLContext.getInstance("SSLv3");
4.用指定的密钥库和可信任证书库初始化SSLContext
sslcontext.init(getKeyManagers(), getTrustManagers(), null);
5.创建SSLServerSocketFactory类型的对象
ServerSocketFactory ssf = sslcontext.getServerSocketFactory();
6.由SSLServerSocketFactory创建ServerSocket对象
SSLServerSocket serversocket = (SSLServerSocket)ssf.createServerSocket(HTTPS_PORT);
7.等待客户连接
Socket client = listen.accept();
listen为ServerSocket的对象
8.连接后,建立输入和输出流,这样就建立了通信通道
同理,客户端要实现对服务器的验证,大致也需要同样的步骤,这里就不再专门说明。
总结:安全套接字和一般套接字实现的差异
SSL编程和基于Socket的编程类似,首先创建ServerSocket对象,传入端口号,然后执行ServerSocket 对象的accept()方法获取Socket类型的对象,并侦听端口以等待客户程序的服务器连接。最后通过Socket类型的对象获得获得输入和输出流,通过输入和输出流与客户程序进行通信。SSL编程和基于Socket的编程不同的地方在于其ServerSocket对象是通过一个特殊的对象——SSLServerSocketFactory类型的对象创建的,这样以后的输入和输出流将自动按照SSL协议指定的方法交换密钥并对数据进行加密。此外,需要指定包含证书的密钥库,以便客户程序确定SSL服务器是否可靠。
参考文献:
[1]徐迎哓 java安全性编程实例 清华大学出版社 2004年4月 238-283
[2]冯睿 用JSSE定制SSL连接 www.ccidnet.com 2002.10.24
[3]李文军 Java keytool工具的作用及使用方法 计算机世界 2002-7-8
[4] 中国金融认证中心 PKI和SSL协议介绍 www.cfca.com.cn
[5]谷和启 公钥基础设施PKI技术与应用发展 赛迪网 2003.11.10
[6] Overlord_Kahn liwrml翻译 加密概念和PKI基础知识简述 赛迪网 2003年10月16日
[7] Qusay H. Mahmoud 著 边城狂人 译 用J2SE 1.4进行Internet安全编程 计算机世界网球2002-12-2
[8]中国IT认证实验室 SSL/TLS/WTLS原理 2003-1-14