ITPub博客

首页 > 应用开发 > IT综合 > [转载]Internet安全编程----客户端

[转载]Internet安全编程----客户端

原创 IT综合 作者:ccainiao1 时间:2007-01-31 23:27:58 0 删除 编辑

客户端

  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,在客户端和服务器端之间建立安全的数据通道。   

[@more@]

在这篇文章的第一部分 (服务器端),作者已经详细说明了 SSL 和 JSSE,并且说明了如何开发服务器端支持 SSL 应用程序。那一部分中我们开发了一个 HTTPS 服务器,这是一个非常有用的应用程序,在这一部分中同样会用到它。  在这篇文章涉及到客户端的内容,它首先简述 JSSE,然后会做这样一些事情在客户端使用 JSSE API 一步步的开发一个支持 SSL 的客户端应用程序 开发简单的支持 SSL 的客户端应用程序 从服务器端导出证书并在客户端导入 开发一个支持 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 的实例:调用 SSLSocketFactory.getDefault 来获得默认的工厂。默认的工厂被配置为只允许服务器端验证 (不允许客户端验证)。注意许多电子商务网站不需要客户端验证。 使用指定的配置来构造一个新的工厂 (这不在本文讲述的范围内)。   建立 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("", 443);
使用低层的 SSL 套接字  现在,让我们看一个使用低层套接字在 HTTPS 服务器上打开一个 SSL 套接字连接的完整例子。在这个例子中,打开了一个到 HTTPS 服务器的 SSL 套接字连接,并且读入默认文档的内容。示例代码 1 展示了这个应用程序,其中用于打开 SSL 套接字的代码已经加黑显示了。你将会看到,应用程序中其余代码就是常规的输入/输出流代码。  代码示例 1:ReadHttpsURL1

import java.net.*;
import javax.net.*;
import javax.net.ssl.*;

public class ReadHttpsURL1 {
static final int HTTPS_PORT = 443;

public static void main(String argv[]) throws Exception {
if (argv.length != 1) {
System.out.println("Usage: java ReadHttpsURL1 ");
System.exit(0);
}

// Get a Socket factory
SocketFactory factory = SSLSocketFactory.getDefault();

// Get Socket from factory
Socket socket = factory.createSocket(argv[0], HTTPS_PORT);

BufferedWriter out
= new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
BufferedReader in
= new BufferedReader(new InputStreamReader(socket.getInputStream()));
out.write("GET / HTTP/1.0nn");
out.flush();

String line;
StringBuffer sb = new StringBuffer();
while((line = in.readLine()) != null) {
sb.append(line);
}
out.close();
in.close();
System.out.println(sb.toString());
}
}
  用这个应用程序进行实验:拷贝 ReadHttpsURL1 类的代码并粘贴到一个新文件中,将该文件改名为 ReadHttpsURL1.java,并保存在一个你指定的目录下。 使用 javac 编译 ReadHttpsURL1.java。 运行 ReadHttpsURL1 并提供一个域名作为参数,如:
Prompt> java ReadHttpsURL1
几秒种后,你会看到许多 HTML 代码显示在屏幕上。注意,即使我们提供的是域名 ,我们打开的连接也是 https://,这是因为我们使用的端口号 443 是 HTTPS 的默认端口号。   再试试另一个例子,如:

Prompt> java ReadHttpsURL1
  这次运行会抛出如下所示的异常,你能猜到是为什么吗?

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)
  缘于一个很好的理由,它不能运行??因为远端的服务器发送了一个客户端不认识的证书。我在本文的第一部分提到过,当客户端连接服务器的时候,服务器发送它的证书到客户端请求验证。这样,第一个例子中,你进入了 ,服务器的确发送了证书,但 Java 检查了默认的证书库并认出了这个证书是由可信任的 CA 产生的,默认情况下,Java 信任这个 CA。第二个例子中,你进入的是 ,那个网端的证书不是它自己产生的,就是由一个 Java 不知道的 CA 产生的,因此不受信任。


--------------------------------------------------------------------------------
注意,如果系统时钟没有设置正确,那么它的时间就可能在证书的有效期之外,服务器会认为证书无效并抛出 CertificateException 异常。
--------------------------------------------------------------------------------
  为了让示例正确运行,你得从 导入证收到 Java 信任的证书库中。导出和导入证书  为了解释清楚如何输出和输入证书,我会使用我自己的 HTTPS 服务器。这个服务器在第一部分中讨论过。然后,跟着下面的内容开始:运行 HTTPS 服务器,像在第一部分中讨论的那样。 运行 ReadHttpsURL1:java ReadHttpsURL1 localhost。你同样会得到上面所述的异常。 使用下面的 keytool 命令导出服务器证书: 从 serverkeys 文件中导出别名为 qusay 的证书 将导出的证书保存在 server.cert 文件中,这个文件会由 keytool 创建如你看到的那样,我根据要求输入了密码。成功输入密码之后,服务器证书被成功的导出并保存在 server.cert 中。
Prompt> keytool -export -keystore serverkeys -alias qusay -file server.cert

Enter keystore password: hellothere
Certificate stored in file
将文件 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
现在运行 ReadHttpsURL1 并告诉它哪里能找到证书。使用下面的命令:
Prompt> java -Djavax.net.ssl.trustStore=trustedcerts ReadHttpsURL1 localhost
这将会与你的 HTTPS 服务器联接、校验证书,如果正确,它会下载默认页面 index.html。

--------------------------------------------------------------------------------
注意:信任管理器负责决定远端的证书是否值得信任。它使用下面的规则: 如果 javax.net.sll.trustStore 系统属性指定了信任库,那么信任管理器会使用提供的文件来检查证书。如果那个系统属性存在但指定的文件不存在,那么就没有使用任何信任库,会抛出一个 CertificateException 异常。 如果 javax.net.sll.trustStore 系统属性没有定义,那么它会去寻找默认的信任库: 如果在你的 java.home 目录的 lib/security 子目录下存在名为 jssecacerts 的信任库,那么使用的就是它。 如果 jssecacerts 不存在,但是 cacerts 存在 (它随 J2SDK 一起发行,含有数量有限的可信任的基本证书),使用的就是 cacerts。  在我的 Windows 2000 客户机中,java.home 目录是 c:Program Filejavajre1.4.1libsecurity,在上例中,如果你将 trustedcerts 更名为 jssecacerts 并将其移动到 lib/security 子目录中,那么你以后就不需要在命令行指定 javax.net.ssl.trustStore 属性了。  如果你不知道 java.home 在哪里,这里有一小段代码可以让你找到它:

public class FindJavaHome {
public static void main(String argv[]) {
System.out.println(System.getProperty("java.home"));
}
}

--------------------------------------------------------------------------------

URL
类  示例代码 1 中的 ReadHttpsURL1 使用低层的套接字打开到 SSL 服务器的连接。这样做有一个缺点,如果不进行一番解析,我们就不能在命令行清楚的写出像 https:// 这样的 URL。这里有一个更简单的办法在客户端应用程序中使用 SSL 和 JSSE。  java.net.URL 类支持 HTTPS 地址。例如,下面的代码段创建一个 HTTPS 地址并建立一个输入流的读入器:

URL url = new URL("https://");
BufferedReader in
= new BufferedReader(new InputStreamReader(url.openStream()));
  是不是很简单?我希望当你学习 Java 的新东西时,你能欣赏到它的美好之处。  示例代码 1 中的 ReadHttpsURL1 可以由下面使用了 URL 类的示例代码 2 代替:  示例代码 2:ReadHttpsURL2.java

import java.net.*;
import java.io.*;

public class ReadHttpsURL2 {
public static void main(String argv[]) throws Exception {
if(argv.length != 1) {
System.out.println("Usage: java ReadHttpsURL2 ");
System.exit(0);
}

URL url = new URL(argv[0]);
BufferedReader in
= new BufferedReader(new InputStreamReader(url.openStream()));

String line;
StringBuffer sb = new StringBuffer();
while ((line = in.readLine()) != null) {
sb.append(line);
}
in.close();
System.out.println(sb.toString());
}
}
  如果你想试试 ReadHttpsURL2,执行它的命令和上面讨论的类似。注意,无论如何,既然我们使用 URL 类,你就能在命令行指定 URL,包括协议的名称。这里是一个例子:

Prompt> java ReadHttpsURL2 https://localhost
开发一个支持 SSL 的网页浏览器  我们开发一个支持 SSL 的网页浏览器作为一个完整的例子。该浏览器要做下面的工作:用户输入 URL,浏览器能接收它。 浏览器能打开到 URL 指定主机的连接。 浏览器能发送 HTTP 命令。 浏览器会等待 HTTP/HTTPS 服务器的回应。 浏览器能接收 HTML 回应。 浏览器能解析 HTML 并显示出页面。   我们创建的浏览器要能处理任何 URL 如 HTTP、HTTPS、FTP 等。注意我使用工具类 javax.swing.text.html.HTMLEditorKit 来解析 HTML,它提供了对 HTML 3.2 的支持。  示例代码 3 中展示了这个浏览器,QBrowser,的代码。注意 QBrowser 实现了 Runnable 接口。我这样做是因为这个浏览器没有提供“停止”按钮。  示例代码 3:QBrowser.java

import java.io.*;
import java.net.*;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

public class QBrowser implements ActionListener, Runnable {
private JFrame frame;
private JButton go;
private JEditorPane content;
private JTextField url;
private JLabel statusLine;

// default constructor
public QBrowser () {
buildBrowserInterface();
}

private void buildBrowserInterface() {
frame = new JFrame("Q's Browser");
// on close, exit the application using System.exit(0);
frame.setDefaultCloseOperation (3);

url = new JTextField("", 25);
go = new JButton("Go Get It");
go.addActionListener(this);

JPanel controls = new JPanel(new FlowLayout ());
controls.add(new JLabel("URL:"));
controls.add(url);
controls.add(go);
content = new JEditorPane();
content.setEditable(false);
// HTML text. Use the kit in the class javax.swing.text.html.HTMLEditorKit, which
// provides support for HTML 3.2
content.setContentType("text/html");
content.setText("

Q's Browser

Copyright (c) 2002 Qusay H. Mahmoud

");
statusLine = new JLabel("Initialization Complete");

JPanel panel = new JPanel(new BorderLayout (0, 2));
frame.setContentPane(panel);

panel.add(controls, "North");
panel.add(new JScrollPane (content), "Center");
panel.add(statusLine, "South");
frame.pack();
frame.setVisible(true);
}

/**
* You cannot stop a download with QBrowser
* The thread allows multiple downloads to start
* concurrently in case a download freezes
*/
public void actionPerformed (ActionEvent event) {
Thread thread = new Thread(this);
thread.start();
}
// this is the Thread's run method
public void run () {
try {
String str = url.getText();
URL url = new URL(str);
readURL(url);
} catch (IOException ioe) {
statusLine.setText("Error: "+ioe.getMessage());
showException(ioe);
}
}

private void showException(Exception ex) {
StringWriter trace = new StringWriter ();
ex.printStackTrace (new PrintWriter (trace));
content.setContentType ("text/html");
content.setText ("

" + ex + "

" + trace + "
");
}

/**
* The URL class is capable of handling http:// and https:// URLs
*/
private void readURL(URL url) throws IOException {
statusLine.setText("Opening " + url.toExternalForm());
URLConnection connection = url.openConnection();
StringBuffer buffer = new StringBuffer();
BufferedReader in=null;
try {
in = new BufferedReader(new InputStreamReader(connection.getInputStream()));
String line;
while ((line = in.readLine()) != null) {
buffer.append(line).append('n');
statusLine.setText("Read " + buffer.length () + " bytes...");
}
} finally {
if(in != null) in.close();
}
String type = connection.getContentType();
if(type == null) type = "text/plain";
statusLine.setText("Content type " + type);
content.setContentType(type);
content.setText(buffer.toString());
statusLine.setText("Done");
}

public static void main (String[] args) {
QBrowser browser = new QBrowser();
}
}  既然 QBrowser 使用 URL 类,它就可以处理 HTTP 和 HTTPS 请求。你可以使用 HTTP 和 HTTPS 地址测试 QBrowser。这里是一些测试:

1.
请求 http://www.javacourses.com,你会看到如图 1 所示的内容。


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


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


HttpsURLConnection
类  这个类存在于 javax.net.ssl 包中,它扩展了 java.net.HttpURLConnection,以支持 HTTPS 描述的一些特性。它能够通过 SSL/TLS 套接字建立安全通道来请求/获取数据。示例代码 4 展示了一个小型客户端,它使用 HttpsURLConnection 类从 HTTPS 服务器下载文档。  示例代码 4:ReadHttpsURL3.java

import java.io.*;
import java.net.*;
import javax.net.ssl.*;

public class ReadHttpsURL3 {
public static void main(String[] argv) throws Exception {
URL url = new URL(argv[0]);
HttpsURLConnection connection = (HttpsURLConnection) url.openConnection();
connection.setDoOutput(true);
BufferedReader in
= new BufferedReader(new InputStreamReader(connection.getInputStream()));
String line;
while ((line = in.readLine()) != null) {
System.out.println(line);
}
in.close();
}
}
  现在试试 ReadHttpsURL3,完成上面讨论的内容。注意,无论如何,既然我们使用 URL 类,你就能在命令行指定 URL,包括协议的名称。这里是一个例子:

Prompt> java ReadHttpsURL3 https://
  HttpsURLConnection 有一个非常有趣的特点:一旦获得了连接,你就可以在网络连接之前使用一些有用的参数对其进行配置,如 HostnameVerifier。HostnameVerifier 是一个接口,它申明了方法:public boolean verify (String hostname, SSLSession session)。而且,它像下面所述的那样工作:如果 SSL/TLS 标准主机名校验逻辑失败,执行过程中会调用回调类的 verify 方法。回调类是实现了 HostnameVerifier 接口的类。 如果回调类检查到主机名可以接受,则允许连接,否则,连接会被终止。   回调类遵循的规则即可以是基本的验证方法,也可以依赖其它验证方法。这里说明了如何实现:

public class MyVerified implements HostnameVerifier {
public boolean verify(String hostname, SSLSession session) {
// pop up a dialog box
// ...
// return either true or false
}
}
  现在,可以这样使用它:

HttpsURLConnection connection = (HttpsURLConnection) url.openConnection();
connection.setHostnameVerifier(new MyVerifier());
信任管理器  一个 SSL 客户端,如网页浏览器,连接到 SSL 服务器 (如

来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/8672542/viewspace-896351/ ,如需转载,请注明出处,否则将追究法律责任。

请登录后发表评论 登录
全部评论
  • 博文量
    38
  • 访问量
    5308636