服务器端
任何在计算机网络或者 internet 中传输的消息都可能被拦截,其中不乏一些比较敏感的内容,如信用卡号或者其它一些私人数据。为了更好的在企业环境和电子商务中使用 internet,应用软件必须使用加 密、验证和安全的通信协议来保护用户的数据安全。安全超文本传输协议 (secure hypertext transfer protocol, https) 是建立于安全套接层 (secure sockets layer, ssl) 上的 http,它已经成功的应用于电子商务。
java 安全套接扩展 (java secure socket extension, jsse) 使 internet 安全通信成为现实。它是 100% 纯 java 实现的 ssl 框架。这个包让 java 开发人员能够开发安全的网络应用;为基于 tcp/ip 的何应用协议,如 http、ftp、telnet、或者 nttp,在客户端和服务器端之间建立安全的数据通道。
jsse 已经整合在 java 2 sdk 标准版本 1.4 (j2se 1.4) 中了,这真是一个好消息。这意味着只要你安装了 j2se 1.4,不需要再下载其它的包,就可以创建基于 ssl 的 internet 应用程序了。这个系列的文章共有 2 篇,它是一本关于为今后的市场开发安全 interent 应用的手册。这篇文章主要是讲的服务器端,而下一篇是讲客户端的。这篇文章从概览 ssl 开始,然后告诉你如何进行下列内容:
使用 jsse 的 api
在你的 c/s 应用程序中结合 jsse
开发一个简单的 http 服务器
让 http 服务器能够处理 https 请求
使用包含在 j2se 中的 keytool 产生自己的证书
开发、配置和运行一个安全的 http 服务器
概览 ssl
ssl 协议是 netscape 在 1994 年开发出来的,以允许服务端 (典型的如浏览器) 和 http 服务器之间能通过安全的连接来通信。它加密、来源验证、数据完整性等支持,以保护在不安全的公众网络上交换的数据。ssl 有这样一些版本:ssl 2.0 有安全隐患,现在已经几本上不用了;ssl 3.0 应用则比较广泛;最后,由 ssl 3.0 改进而来的传输层加密 (transport layer security, tls) 已经成为 internet 标准并应用于几乎所有新近的软件中。
在数据传播之前,加密技术通过将数据转变成看起来毫无意义的内容来保护数据不被非法使用。其过程是:数据在一端 (客户端或者服务器端) 被加密,传输,再在另一端解密。
来源认证是验证数据发送者身份的一种办法。浏览器或者其它客户端第一次尝试与网页服务器进行安全连接之上的通信时,服务器会将一套信任信息以证书的形式呈现出来。
证书由权威认证机构 (ca)――值得信赖的授权者发行和验证。一个证书描述一个人的公钥。一个签名的文档会作出如下保证:我证明文档中的这个公钥属于在该文档中命名的实体。签名(权威认证机构)。目前知名的权威认证机构有 verisign,entrust 和 thawte 等。注意现在使用的 ssl/tls 证书是 x.509 证书。
数据完整性就是要确保数据在传输过程中没有被改变。
ssl 和 tcp/ip 协议的层次
ssl 是名符其实的安全套接层。它的连接动作和 tcp 的连接类似,因此,你可以想象 ssl 连接就是安全的 tcp 连接,因为在协议层次图中 ssl 的位置正好在 tcp 之上而在应用层之下,如图 1 所示。注意到这点很重要。但是,ssl 不支持某些 tcp 的特性,比如频带外数据。

图 1: ssl 和 tcp/ip 协议的的层次
可交流的加密技术
ssl 的特性之一是为电子商务的事务提供可交流的加密技术和验证算法提供标准的方法。ssl 的开发者认识到不是所有人都会使用同一个客户端软件,从而不是所有客户端都会包括任何详细的加密算法。对于服务器也是同样。位于连接两端的的客户端和服务器在初始化“握手”的时候需要交流加密和解密算法(密码组)。如果它们没有足够的公用算法,连接尝试将会失败。
注意当 ssl 允许客户端和服务器端相互验证的时候,典型的作法是只有服务器端在 ssl 层上进行验证。客户端通常在应用层,通过 ssl 保护通道传送的密码来进行验证。这个模式常用于银行、股份交易和其它的安全网络应用中。
ssl 完全“握手”协议如图 2 所示。它展示了在 ssl “握手”过程中的信息交换顺序。

图 2:ssl “握手”协议
这些消息的意思如下:
1. clienthello:发送信息到服务器的客户端,这些信息如 ssl 协议版本、会话 id 和密码组信息,如加密算法和能支持的密匙的大小。
2. serverhello:选择最好密码组的服务器并发送这个消息给客户端。密码组包括客户端和服务器支持。
3. certificate:服务器将包含其公钥的证书发送给客户端。这个消息是可选的,在服务器请求验证的时候会需要它。换句话说,证书用于向客户端确认服务器的身分。
4. certificate request: 这个消息仅在服务器请求客户端验证它自身的时候发送。多数电子商务应用不需要客户端对自身进行。
5. server key exchange:如果证书包含了服务器的公钥不足以进行密匙交换,则发送该消息。
6. serverhellodone:这个消息通知客户端,服务器已经完成了交流过程的初始化。
7. certificate:仅当服务器请求客户端对自己进行验证的时候发送。
8. client key exchage:客户端产生一个密匙与服务器共享。如果使用 rivest-shamir-adelman (rsa) 加密算法,客户端将使用服务器的公钥将密匙加密之后再发送给服务器。服务器使用自己的私钥或者密钥对消息进行解密以得到共享的密匙。现在,客户端和服务器共享着一个已经安全分发的密匙。
9. certificate verify:如果服务器请求验证客户端,这个消息允许服务器完成验证过程。
10. change cipher spec:客户端要求服务器使用加密模式。
11. finished:客户端告诉服务器它已经准备好安全通信了。
12. change cipher spec:服务器要求客户端使用加密模式。
13. finished:服务器告诉客户端它已经准备好安全通信了。这是 ssl “握手”结果的标志。
14. encrypted data:客户端和服务器现在可以开发在安全通信通道上进行加密信息的交流了。
jsse
java 安全套接扩展 (jsse) 提供一个框架及一个 100% 纯 java 实现的 ssl 和 tls 协议。它提供了数据加密、服务器验证、消息完成性和可选的客户端验证等机制。jsse 的引人之外就是将复杂的、根本的加密算法抽象化了,这样就降低了受到敏感或者危险的安全性攻击的风险。另外,由于它能将 ssl 无缝地结合在应用当然,使安全应用的开发变得非常简单。jsse 框架可以支撑许多不同的安全通信协议,如 ssl 2.0 和 3.0 以及 tls 1.0,但是 j2se v1.4.1 只实现了 ssl 3.0 和 tls 1.0。
jsse 编程
jsse api 提供了扩充的网络套接字类、信用和密匙管理,以及为简化套接字创建而设计的套接字工厂框架,以此扩充 java.security 和 java.net 两个包。这些类都包含在 javax.net 和 javax.net.ssl 包中。
sslsocket 和 sslserversocket
javax.net.ssl.sslsocket 是 java.net.socket 的子类,因此他支持所有标准 socket 的方法,和一些为安全套接字新增加的方法。javax.net.ssl.sslserversocket 类与 sslsocket 类相似,只是它用于创建服务器套接子,而 sslsocket 不是。
创建一个 sslsocket 实例有如何两种方法:
1. 用 sslsocketfactory 实例执行 createsocket 方法来创建。
2. 通过 sslserversocket 的 accept 方法获得。
sslsocketfactory 和 sslserversocketfactory
javax.net.ssl.sslsocketfactory 类是用于创建安全套接字的对象工厂。javax.net.ssl.sslserversocketfactory 也是这样的工厂,但它用于创建安全的服务器套接字。
可以通过如下方法获得 sslsocketfactory 实例:
1. 执行 sslsocketfactory.getdefault 方法获得一个默认的工厂。
2. 通过特定的配置行为构造一个新的工厂。
注意默认的工厂的配置只允许服务器验证。
使现有的 client/server 应用变得安全
在现有的 c/s 应用中整合 ssl 以使其变得安全比较简单,使用几行 jsse 代码就可以做到。为了使服务器变得安全,下面的例子中加黑显示的内容是必须的:
|
为了使客户端变得安全,下面的例子中加黑显示的内容是必须的:
|
sunjsse 提供者
j2se v1.4.1 和一个 jsse 提供者,sunjsse 一起发布。sunjsse 安装并预登记了 java 的加密体系。请把 sunjsse 作为一个实现的名字来考虑,它提供了 ssl v3.0 和 tls v1.0 的实现,也提供了普通的 ssl 和 tls 密码组。如果你想找到你的实现 (这里是 sunjsse) 所支持的密码组列表,可以调用 sslsocket 的 getsupportedciphersuites 方法。然而,不是所有这些密码组都是可用的。为了找出那些是可用的,调用 getenabledciphersuites 方法。这个列表可以用 setenabledciphersuites 方法来更改。
一个完整的例子
我发现使用 jsse 开发最复杂的事情关系到系统设置以及管理证书和密匙。在这个例子中,我演示了如何开发、配置和运行一个完整的支持 get 请求方法的 http 服务器应用。
http 概览
超文本传输协议 (hypertext transfer protocol, http) 是一个“请求-回应”的应用协议。这个协议支持一套固定的方法如 get、post、put、delete 等。一般用 get 方法向服务器请求资源。这里有两个 get 请求的例子:
get / http/1.0
get /names.html http/1.0
不安全的 http 服务器
为了开发一个 http 服务器,你得先搞明白 http 协议是如何工作的。这个服务器是一个只支持 get 请求方法的简单服务器。代码示例 1 是这个例子的实现。这是一个多线程的 http 服务器,processconnection 类用于执行不同线程中新的请求。当服务器收到一个来自浏览器的请求时,它解析这个请求并找出需要的文档。如果被请求的文档在服务器上可用,那么被请求的文档会由 shipdocument 方法送到服务器。如果被请求的文档没有打开,那么送到服务器的就是出错消息。
代码示例 1:httpserver.java
|
实验一下 httpserver 类:
1. 将 httpserver 的代码保存在文件 httpserver.java 中,并选择一个目录把它存放在那里。
2. 使用 javac 编译 httpserver.java
3. 建立一些 html 文件作为例子,要有一个“index.html”,因为它是这个例子中默认的 html 文档。
4. 运行 httpserver。服务器运行时使用 8080 端口。
5. 打开网页浏览器,并发出请求:http://localhost:8080 或者 http://127.0.0.1:8080/index.html。
注意:你能想到 httpserver 可能接收到一些恶意的 url 吗?比如像 http://serverdomainname:8080/../../etc/passwd 或者 http://serverdomainname:8080//somefile.txt 等。作为一个练习,修改 httpserver 以使其不允许这些 url 的访问。提示:写你自己的 securitymanager 或者使用 java.lang.securitymanager。你可以在 main 方法的第一行添加语句 system.setsecuritymanager(new java.lang.securitymanager) 来安装这个安全的管理器。试试吧!
扩展 httpserver 使其能够处理 https://url
现在,我要们修改 httpserver 类,使它变得安全。我希望 http 服务器能处理 https://url 请求。我在前面就提到过,jsse 让你可以很容易的把 ssl 整合到应用中去。
创建一个服务器证书
就像我前面提到的那样,ssl 使用证书来进行验证。对于需要使用 ssl 来保证通信安全的客户端和服务器,都必须创建证书。jsse 使用的证书要用与 j2se 一起发布的 java keytool 来创建。用下列命令来为 http 服务器创建一个 rsa 证书。
prompt> keytool -genkey -keystore serverkeys -keyalg rsa -alias qusay
这个命令会产生一个由别名 qusay 引用的证书,并将其保存在一个名为 serverkeys 的文件中。产生证书的时候,这个工具会提示我们一些信息,如下面的信息,其中加黑的内容是我写的。
enter keystore password: hellothere
what is your first and last name?
[unknown]: ultra.domain.com
what is the name of your organizational unit?
[unknown]: training and consulting
what is the name of your organization?
[unknown]: javacourses.com
what is the name of your city or locality?
[unknown]: toronto
what is the name of your state or province?
[unknown]: ontario
what is the two-letter country code for this unit?
[unknown]: ca
is cn=ultra, ou=training and consulting,
o=javacourses.com, l=toronto, st=ontario, c=ca correct?
[no]: yes
enter key password for
(return if same as keystore password): hiagain
正如你所看到的,keytool 提示为 keystore 输入密码,那是因为让服务器能访问 keystore 就必须让它知道密码。那工具也要求为别名输入一个密码。如果你愿意,这些密码信息能由 keytool 从命令行指定,使用参数 -storepass 和 -keypass 就行了。注意我使用了“ultra.domain.com”作为姓名,这个名字是为我的机器假想的一个名字。你应该输入服务器的主机名或者 ip 地址。
在你运行 keytool 命令的时候,它可能会花几秒钟的时间来产生你的密码,具体速度得看你机器的速度了。
既然我为服务器创建了证书,现在可以修改 httpserver 使其变得安全了。如果你检查 httpserver 类,你会注意到 getserver 方法用来返回一个服务器套接子。也就是说,只需要修改 getserver 方法让它返回一个安全的服务器套接字就可以了。在代码示例 2 中加黑的部分就是所做的改变。请注意我将端口号改成了 443,这是 https 默认的端口号。还有一点非常值得注意:0 到 1023 之间的端口号都是保留的。如果你在不同的端口运行 httpsserver,那么 url 应该是:https://localhost:portnumber。但如果你在 443 端口运行 httpsserver,那么 url 应该是:https://localhost。
示例代码 2:httpsserver.java
|
这几行:string keystore = "serverkeys";
char keystorepass[] = "hellothere".tochararray();
char keypassword[] = "hiagain".tochararray();
指定了 keystore 的名字、密码和密匙密码。直接在代码中写出密码文本是个糟糕的主意,不过我们可以在运行服务器的时候在命令行指定密码。
getserver 方法中的其它 jsse 代码:
它访问 serverkeys keystore,jsk 是 java keystore (一种由 keytool 产生的 keystore)。
用 keymanagerfactory 为 keystore 创建 x.509 密匙管理。
sslcontext 是实现 jsse 的环境。用它来创建可以创建 sslserversocket 的 serversocketfactory。虽然我们指定使用 ssl 3.0,但是返回来的实现常常支持其它协议版本,如 tls 1.0。旧的浏览器中更多时候使用 ssl 3.0。
注意默认情况下不需要客户端的验证。如果你想要服务器请求客户端进行验证,使用:
serversocket.setneedclientauth(true).
现在用 httpsserver 类做个实验:
1. 将 httpsserver 和 processconnection 两个类 (上面的代码) 保存在文件 httpsserver.java 中。
2. 让httpsserver.java 与 keytool 创建的 serverkyes 文件在同一目录。
3. 使用 javac 编译 httpsserver。
4. 运行 httpsserver。默认情况下它应该使用 443 端口,不过如果你不能在这个端口上使用它,请选择另一个大于 1024 的端口号。
5. 打开网页浏览器并输入请求:https://localhost 或者 https://127.0.0.1。这是假译服务器使用 443 端口的情况。如果不是这个端口,那么使用:use: https://localhost:port
你在浏览器中输入 https://url 的时候,你会得到一个安全警告的弹出窗口,就像图 3 那样。这是因为 http 服务器证书是自己产生的。换句话说,它由未知的 ca 创建,在你的浏览器保存的 ca 中没有找到这个 ca。有一个选项让你显示证书 (检查它是不是正确的证书以及是谁签的名) 和安装该证书、拒绝该证书或者接受该证书。

图 3:由未知 ca 颁发的服务器证书
注意:在内部的私有系统中产生你自己的证书是个很好的主意。但在公共系统中,最好从知名的 ca 处获得证书,以避免浏览器的安全警告。
如果你接受证书,你就可以看到安全连接之后的页面。以后访问同一个网站的时候浏览器就不再会弹出安全警告了。注意有许多网站使用 https,而证书是自己产生或者由不知名的 ca 产生的。例如,https://www.jam.ca。如果你没访问过这个网页,你会看到一个像图 3 一样的安全警告。
注意:你接受证书以后,它只对当前的会话有效,也就是说,如果你完全退出浏览器后,它就失效了。netscape 和 microsoft internet explorer (msie) 都允许你永久保证证书。在 msie 中的作法是:选择图 3 所示的“view certificate”并在新开的窗口中选择“install certificate”。
总结
这篇文章谈到了 ssl 并描述了 jsse 框架及其实现。文中的例子可以说明把 ssl 整合到你的 c/s 应用中是一件很容易的事情。文中给出了一个安全 http 服务器的例子,你可以使用它来进行实验。文中还介绍了 jsse api 以及可以发生 https 请求的网页浏览器。
用j2se 1.4进行internet安全编程(下)
客户端
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 在哪里,这里有一小段代码可以让你找到它:
|
url 类
示例代码 1 中的 readhttpsurl1 使用低层的套接字打开到 ssl 服务器的连接。这样做有一个缺点,如果不进行一番解析,我们就不能在命令行清楚的写出像 https://www.jam.ca 这样的 url。这里有一个更简单的办法在客户端应用程序中使用 ssl 和 jsse。
java.net.url 类支持 https 地址。例如,下面的代码段创建一个 https 地址并建立一个输入流的读入器:
url url = new url("https://www.sun.com");
bufferedreader in
= new bufferedreader(new inputstreamreader(url.openstream()));
是不是很简单?我希望当你学习 java 的新东西时,你能欣赏到它的美好之处。
示例代码 1 中的 readhttpsurl1 可以由下面使用了 url 类的示例代码 2 代替:
示例代码 2:readhttpsurl2.java
|
如果你想试试 readhttpsurl2,执行它的命令和上面讨论的类似。注意,无论如何,既然我们使用 url 类,你就能在命令行指定 url,包括协议的名称。这里是一个例子:
prompt> java readhttpsurl2 https://localhost
开发一个支持 ssl 的网页浏览器
我们开发一个支持 ssl 的网页浏览器作为一个完整的例子。该浏览器要做下面的工作:
1.用户输入 url,浏览器能接收它。
2.浏览器能打开到 url 指定主机的连接。
3.浏览器能发送 http 命令。
4.浏览器会等待 http/https 服务器的回应。
5.浏览器能接收 html 回应。
6.浏览器能解析 html 并显示出页面。
我们创建的浏览器要能处理任何 url 如 http、https、ftp 等。注意我使用工具类 javax.swing.text.html.htmleditorkit 来解析 html,它提供了对 html 3.2 的支持。
示例代码 3 中展示了这个浏览器,qbrowser,的代码。注意 qbrowser 实现了 runnable 接口。我这样做是因为这个浏览器没有提供“停止”按钮。
示例代码 3:qbrowser.java
|
既然 qbrowser 使用 url 类,它就可以处理 http 和 https 请求。你可以使用 http 和 https 地址测试 qbrowser。这里是一些测试:
1、请求 http://www.javacourses.com,你会看到如图 1 所示的内容。

图 1:http://www.javacourses.com
2、请求 https://www.jam.ca,结果抛出了异常。因为这个网页服务器的证书不受信任并且不能在默认页中找到,所以它抛出如图 2 所示的异常。

图 2:https://www.jam.ca
3、请求 https://localhost,这里运行着第一部分中写的 httpserver。注意,如果你使用命令 java qbrowser 来运行 qbrowser,而服务器的证书导出后被导入默认文件 jssecacerts,那么应该将该文件拷贝到 java.home 目录的 lib/security 子目录中。如果证书被导入了其它文件,你可以使用 truststore 选项,如:java -djavax.net.ssl.truststore=file qbrowser。使用其实任何一种方法,浏览器都会工作,并且你可以看到如图 3 所示的默认页面。

图 3:https://localhost
httpsurlconnection 类
这个类存在于 javax.net.ssl 包中,它扩展了 java.net.httpurlconnection,以支持 https 描述的一些特性。它能够通过 ssl/tls 套接字建立安全通道来请求/获取数据。示例代码 4 展示了一个小型客户端,它使用 httpsurlconnection 类从 https 服务器下载文档。
示例代码 4:readhttpsurl3.java
|
现在试试 readhttpsurl3,完成上面讨论的内容。注意,无论如何,既然我们使用 url 类,你就能在命令行指定 url,包括协议的名称。这里是一个例子:
prompt> java readhttpsurl3 https://www.sun.com
httpsurlconnection 有一个非常有趣的特点:一旦获得了连接,你就可以在网络连接之前使用一些有用的参数对其进行配置,如 hostnameverifier。hostnameverifier 是一个接口,它申明了方法:public boolean verify (string hostname, sslsession session)。而且,它像下面所述的那样工作:
如果 ssl/tls 标准主机名校验逻辑失败,执行过程中会调用回调类的 verify 方法。回调类是实现了 hostnameverifier 接口的类。
如果回调类检查到主机名可以接受,则允许连接,否则,连接会被终止。
回调类遵循的规则即可以是基本的验证方法,也可以依赖其它验证方法。这里说明了如何实现:
|
现在,可以这样使用它:
httpsurlconnection connection = (httpsurlconnection) url.openconnection();
connection.sethostnameverifier(new myverifier());
信任管理器
一个 ssl 客户端,如网页浏览器,连接到 ssl 服务器 (如 https 服务器) 的时候,https 服务器将自己的证书链交给客户端验证。ssl 规范规定,如果在证书链中发现有无效的证书,客户端应该立即终止连接。一些网页浏览器,如 netscape communicator 和 microsoft internet explorer,询问用户是否忽略无效的证书并继续检查证书链,以确定是否有可能验证通过 https 服务器。使用 javax.net.sll.trustmanager 可以很好的消除这种矛盾,它是 jsse 信任管理器的基础接口。而这些信任管理器则是用来管理可信任的资料以及决定是否接受某个凭证的。典型的信任管理器都支持基于 x.509 的证书,它是 j2dk 的 keytool 可以管理的一个普通的证书格式。
x509trustmanager 接口
javax.net.sll.x509trustmanager 接口扩展了普通的 trustmanager 接口。使用基于 x.509 公钥证书验证方案时,信任管理器必须实现该接口。实现 x509trustmanager 可以创建信任管理器。这里有一个空实现:
|
为了支持远端套接字 x.509 证书,实现了 x509trustmanager 接口的类,其实例要传递给 sslcontext 对象的 init 方法。它作为 ssl 套接字工厂。换句话说,一旦创建了信任管理器且通过 init 方法将其分配给了一个 sslsocket,以后从 sslcontext 创建的 socketfactories 在作信任决策时将使用新的信任管理器。下面的代码段就是个示例:
x509trustmanager xtm = new mytrustmanager()
trustmanager mytm[] = {xtm};
sslcontext ctx = sslcontext.getinstance("ssl");
ctx.init(null,mytm, null );
sslsocketfactory sf = ctx.getsocketfactory();
jsse 调试工具
sun 的 jsse 实现提供了动态调试跟踪支持,使用系统属性 javax.net.debug 即可。jsse 并不正式支持这个特性,但它可以让你看到在 ssl 通信过程中幕后在干什么。这个工具可以通过如下命令使用:
prompt> java -djavax.net.debug=option[debugspecifiers] mysslapp
如果你使用了 help 参数,它就会显示调试选项列表。j2se 1.4.1 中,选项如下:
|
你必须指定参数 ssl 或者 all 中的一个,紧跟 debug 符号。可以使用一个或多个调试说明符,使用“:”或者“,”作为分隔符。说明符不是必须的,但可以增强可读性。这里是一些例子:
prompt> java -djavax.net.debug=all myapp
prompt> java -djavax.net.debug=ssl myapp
prompt> java -djavax.net.debug=ssl:handshake:trustmanager myapp
总结
这篇文章展示了如何使用 jsse (ssl 协议的框架和实现) 开发安全的客户端应用程序。这篇文章中的例子展示了将 ssl 整合到 c/s 应用程序是多么简单的事情。这篇文章中讲到一个网页浏览器,qbrowser,可以处理 http 和 https 请求。
qbrowser 中,如果服务器上,按输入 https 的地址中不存在有效的证书,则会抛出一个异常。你也许想修改 qbrowser 使其能够处理这个异常并且弹出一个窗口询问用户是否愿意下载安装证书,那么你可以把它做为一个练习。1.4.x 的 java 插件使用了 jsse,它有自己的的信任管理器,如果它不能在信任库里找到证书,而弹出窗口提示。
原文:secure internet programming with java 2, standard edition (j2se) 1.4 (part ii: the client side)
参阅:secure internet programming with java 2, standard edition (j2se) 1.4 (part i: the server side)
闽公网安备 35060202000074号