客户端
Java 安全套接扩展 (Java Secure Socket Extension, JSSE) 使 Internet 安全通信成为现实。它是 SSL 3.0 (Secure Socket Layer) 及 TLS 1.0 (Transport Layer Security,由 SSL 3.0 改善而来) 的框架和实现。这个包让 Java 开发人员能够开发安全的网络应用;为基于 TCP/IP 的何应用协议,如 HTTP、FTP、Telnet、或者 NTTP,在客户端和服务器端之间建立安全的数据通道。
在这篇文章的第一部分 (服务器端),作者已经详细说明了 SSL 和 JSSE,并且说明了如何开发服务器端支持 SSL 应用程序。那一部分中我们开发了一个 HTTPS 服务器,这是一个非常有用的应用程序,在这一部分中同样会用到它。
在这篇文章涉及到客户端的内容,它首先简述 JSSE,然后会做这样一些事情
l在客户端使用 JSSE API
l一步步的开发一个支持 SSL 的客户端应用程序
l开发简单的支持 SSL 的客户端应用程序
l从服务器端导出证书并在客户端导入
l开发一个支持 SSL 的网页浏览器
JSSE
Java 安全套接扩展 (JSSE) 提供了 SSL 和 TLS 协议的框架及实现。JSSE 将复杂的、根本的加密算法抽象化了,这样就降低了受到敏感或者危险的安全性攻击的风险。正如你在本文中看到的那样,由于它能将 SSL 无缝地结合在应用当然,使安全应用的开发变得非常简单。JSSE 框架可以支撑许多不同的安全通信协议,如 SSL 2.0 和 3.0 以及 TLS 1.0,但是 J2SE v1.4 只实现了 SSL 3.0 和 TLS 1.0。
用 JSSE 编写客户端应用程序
JSSE API 提供了扩充的网络套接字类、信用和密匙管理,以及为简化套接字创建而设计的套接字工厂框架,以此扩充 java.security 和 java.net 两个包。这些类都包含在 javax.net 和 javax.net.ssl 包中。
javax.net.sll.SSLSocketFactory 类是一个创建安全套接字的对象工厂。可以通过下面两种方法获得 SSLSocketFactory 的实例:
1、调用 SSLSocketFactory.getDefault 来获得默认的工厂。默认的工厂被配置为只允许服务器端验证 (不允许客户端验证)。注意许多电子商务网站不需要客户端验证。
2、使用指定的配置来构造一个新的工厂 (这不在本文讲述的范围内)。
建立 SSLSocketFactory 实例之后,你就可以通过 SSLSocketFactory 实例的 createSocket 方法创建 SSLSocket 对象了。这里有一个例子,该例通过 SSL 端口 443 (这是 HTTPS 的默认端口) 创建套接字并连接到 Sun 的 WWW 服务器。
// Get a Socket factory
SocketFactory factory = SSLSocketFactory.getDefault();
// Get Socket from factory
Socket socket = factory.createSocket("www.sun.com", 443);
使用低层的 SSL 套接字
现在,让我们看一个使用低层套接字在 HTTPS 服务器上打开一个 SSL 套接字连接的完整例子。在这个例子中,打开了一个到 HTTPS 服务器的 SSL 套接字连接,并且读入默认文档的内容。示例代码 1 展示了这个应用程序,其中用于打开 SSL 套接字的代码已经加黑显示了。你将会看到,应用程序中其余代码就是常规的输入/输出流代码。
代码示例 1:ReadHttpsURL1
|
用这个应用程序进行实验
1、拷贝 ReadHttpsURL1 类的代码并粘贴到一个新文件中,将该文件改名为 ReadHttpsURL1.java,并保存在一个你指定的目录下。
2、使用 javac 编译 ReadHttpsURL1.java。
3、运行 ReadHttpsURL1 并提供一个域名作为参数,如:
Prompt> java ReadHttpsURL1 www.sun.com
几秒种后,你会看到许多 HTML 代码显示在屏幕上。注意,即使我们提供的是域名 www.sun.com,我们打开的连接也是 https://www.sun.com,这是因为我们使用的端口号 443 是 HTTPS 的默认端口号。
再试试另一个例子,如:
Prompt> java ReadHttpsURL1 www.jam.ca
这次运行会抛出如下所示的异常,你能猜到是为什么吗?
Exception in thread "main" javax.net.ssl.SSLHandshakeException: java.security.cert.CertificateException: Couldn't find trusted certificate at com.sun.net.ssl.internal.ssl.BaseSSLSocketImpl.a(DashoA6275)
缘于一个很好的理由,它不能运行——因为远端的服务器发送了一个客户端不认识的证书。我在本文的第一部分提到过,当客户端连接服务器的时候,服务器发送它的证书到客户端请求验证。这样,第一个例子中,你进入了 www.sun.com,服务器的确发送了证书,但 Java 检查了默认的证书库并认出了这个证书是由可信任的 CA 产生的,默认情况下,Java 信任这个 CA。第二个例子中,你进入的是 www.jam.ca,那个网端的证书不是它自己产生的,就是由一个 Java 不知道的 CA 产生的,因此不受信任。
注意,如果系统时钟没有设置正确,那么它的时间就可能在证书的有效期之外,服务器会认为证书无效并抛出 CertificateException 异常。
为了让示例正确运行,你得从 www.jam.ca 导入证收到 Java 信任的证书库中。
导出和导入证书
为了解释清楚如何输出和输入证书,我会使用我自己的 HTTPS 服务器。这个服务器在第一部分中讨论过。然后,跟着下面的内容开始:
1、运行 HTTPS 服务器,像在第一部分中讨论的那样。
2、运行 ReadHttpsURL1:java ReadHttpsURL1 localhost。你同样会得到上面所述的异常。
3、使用下面的 keytool 命令导出服务器证书:
o 从 serverkeys 文件中导出别名为 qusay 的证书
o 将导出的证书保存在 server.cert 文件中,这个文件会由 keytool 创建
如你看到的那样,我根据要求输入了密码。成功输入密码之后,服务器证书被成功的导出并保存在 server.cert 中。
Prompt> keytool -export -keystore serverkeys -alias qusay -file server.cert
Enter keystore password: hellothere
Certificate stored in file
4、将文件 server.cert 拷贝到 ReadHttpsURL1 所在的目录。使用 keytool 创建一个新的 keystore 并将服务器的 server.cert 证书导入其中。这里的命令示例:
Prompt> keytool -import -keystore trustedcerts -alias qusay -file server.cert
这个命令会产生下面那样的输出。它要求输入密码,这是一个新的密码,用于 trustedcerts 这个 keystore 的。这个 keystore 由 keytool 创建。在输出信息的最后,它询问我是否愿意相信这个证书,我回答 yes。
Enter keystore password: clientpass
Owner: CN=localhost, OU=Training and Consulting, O=javacourses.com, L=Toronto, ST=Ontario, C=CA
Issuer: CN=localhost, OU=Training and Consulting, O=javacourses.com, L=Toronto, ST=Ontario, C=CA
Serial number: 3dcf988a
Valid from: Mon Nov 11 06:46:18 EST 2002 until: Sun Feb 09 06:46:18 EST 2003
Certificate fingerprints:
MD5: 37:35:4D:3A:2B:7E:B5:09:A5:41:B3:FA:E4:3C:1D:C4
SHA1: CB:7C:77:36:79:A2:37:26:E2:98:61:C2:9D:10:50:69:
99:F9:B9:1B
Trust this certificate? [no]: yes
Certificate was added to keystore
5、现在运行 ReadHttpsURL1 并告诉它哪里能找到证书。使用下面的命令:
Prompt> java -Djavax.net.ssl.trustStore=trustedcerts ReadHttpsURL1 localhost
这将会与你的 HTTPS 服务器联接、校验证书,如果正确,它会下载默认页面 index.html。
注意:信任管理器负责决定远端的证书是否值得信任。它使用下面的规则:
1、如果 javax.net.sll.trustStore 系统属性指定了信任库,那么信任管理器会使用提供的文件来检查证书。如果那个系统属性存在但指定的文件不存在,那么就没有使用任何信任库,会抛出一个 CertificateException 异常。
2、如果 javax.net.sll.trustStore 系统属性没有定义,那么它会去寻找默认的信任库:
如果在你的 java.home 目录的 lib/security 子目录下存在名为 jssecacerts 的信任库,那么使用的就是它。
如果 jssecacerts 不存在,但是 cacerts 存在 (它随 J2SDK 一起发行,含有数量有限的可信任的基本证书),使用的就是 cacerts。
在我的 Windows 2000 客户机中,java.home 目录是 c:\Program File\java\jre1.4.1\lib\security,在上例中,如果你将 trustedcerts 更名为 jssecacerts 并将其移动到 lib/security 子目录中,那么你以后就不需要在命令行指定 javax.net.ssl.trustStore 属性了。
如果你不知道 java.home 在哪里,这里有一小段代码可以让你找到它:
|

