CSDN博客

img ghostmanxp

用J2SE 1.4进行Internet安全编程之二【转载】

发表于2004/10/10 16:33:00  4656人阅读

分类: Java网络编程

 

 

用户

密码

登录 | 注册

 

搜索

 

 

 

 

CIO俱乐部

焦点 | 数据 | CIO生活 | 方案·案例展示中心 | 供求与招投标 | 论坛 | CIO俱乐部章程 

 

J2SE 1.4进行Internet安全编程

 

 

2003-1-11 15:05:26

 

 

Qusay H. Mahmoud 边城狂人

 

 

 

 

 

一个完整的例子

我发现使用 JSSE 开发最复杂的事情关系到系统设置以及管理证书和密匙。在这个例子中,我演示了如何开发、配置和运行一个完整的支持 GET 请求方法的 HTTP 服务器应用。

HTTP 概览

超文本传输协议 (Hypertext Transfer Protocol, HTTP) 是一个请求-回应的应用协议。这个协议支持一套固定的方法如 GETPOSTPUTDELETE 等。一般用 GET 方法向服务器请求资源。这里有两个 GET 请求的例子:

GET / HTTP/1.0 <empty-line>

GET /names.html HTTP/1.0 <empty-line>

不安全的 HTTP 服务器

为了开发一个 HTTP 服务器,你得先搞明白 HTTP 协议是如何工作的。这个服务器是一个只支持 GET 请求方法的简单服务器。代码示例 1 是这个例子的实现。这是一个多线程的 HTTP 服务器,ProcessConnection 类用于执行不同线程中新的请求。当服务器收到一个来自浏览器的请求时,它解析这个请求并找出需要的文档。如果被请求的文档在服务器上可用,那么被请求的文档会由 shipDocument 方法送到服务器。如果被请求的文档没有打开,那么送到服务器的就是出错消息。

代码示例 1HttpServer.java

import java.io.*;

import java.net.*;

import java.util.StringTokenizer;

/**

* This class implements a multithreaded simple HTTP

* server that supports the GET request method.

* It listens on port 44, waits client requests, and

* serves documents.

*/

public class HttpServer {

// The port number which the server

// will be listening on

public static final int HTTP_PORT = 8080;

public ServerSocket getServer() throws Exception {

return new ServerSocket(HTTP_PORT);

}

// multi-threading -- create a new connection

// for each request

public void run() {

ServerSocket listen;

try {

listen = getServer();

while(true) {

Socket client = listen.accept();

ProcessConnection cc = new

ProcessConnection(client);

}

} catch(Exception e) {

System.out.println("Exception:

"+e.getMessage());

}

}

// main program

public static void main(String argv[]) throws

Exception {

HttpServer httpserver = new HttpServer();

httpserver.run();

}

}

class ProcessConnection extends Thread {

Socket client;

BufferedReader is;

DataOutputStream os;

public ProcessConnection(Socket s) { // constructor

client = s;

try {

is = new BufferedReader(new InputStreamReader

(client.getInputStream()));

os = new DataOutputStream(client.getOutputStream());

} catch (IOException e) {

System.out.println("Exception: "+e.getMessage());

}

this.start(); // Thread starts here...this start()

will call run()

}

public void run() {

try {

// get a request and parse it.

String request = is.readLine();

System.out.println( "Request: "+request );

StringTokenizer st = new StringTokenizer( request );

if ( (st.countTokens() >= 2) &&

st.nextToken().equals("GET") ) {

if ( (request =

st.nextToken()).startsWith("/") )

request = request.substring( 1 );

if ( request.equals("") )

request = request + "index.html";

File f = new File(request);

shipDocument(os, f);

} else {

os.writeBytes( "400 Bad Request" );

}

client.close();

} catch (Exception e) {

System.out.println("Exception: " +

e.getMessage());

}

}

/**

* Read the requested file and ships it

* to the browser if found.

*/

public static void shipDocument(DataOutputStream out,

File f) throws Exception {

try {

DataInputStream in = new

DataInputStream(new FileInputStream(f));

int len = (int) f.length();

byte[] buf = new byte[len];

in.readFully(buf);

in.close();

out.writeBytes("HTTP/1.0 200 OK/r/n");

out.writeBytes("Content-Length: " +

f.length() +"/r/n");

out.writeBytes("Content-Type:

text/html/r/n/r/n");

out.write(buf);

out.flush();

} catch (Exception e) {

out.writeBytes("<html><head><title>error</title>

</head><body>/r/n/r/n");

out.writeBytes("HTTP/1.0 400 " + e.getMessage() + "/r/n");

out.writeBytes("Content-Type: text/html/r/n/r/n");

out.writeBytes("</body></html>");

out.flush();

} finally {

out.close();

}

}

}

实验一下 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

然后输入:keytoolgenkey,这个命令会产生一个由别名 qusay 引用的证书,并将其保存在一个名为 .keystore的文件中。产生证书的时候,这个工具会提示我们一些信息,如下面的信息,其中加黑的内容是我写的。

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

示例代码 2HttpsServer.java

import java.io.*;

import java.net.*;

import javax.net.*;

import javax.net.ssl.*;

import java.security.*;

import java.util.StringTokenizer;

/**

* This class implements a multithreaded simple HTTPS

* server that supports the GET request method.

* It listens on port 44, waits client requests

* and serves documents.

*/

public class HttpsServer {

String keystore = ".keystore";

char keystorepass[] = "hellothere".toCharArray();

char keypassword[] = "hiagain".toCharArray();

// The port number which the server will be listening on

public static final int HTTPS_PORT = 443;

public ServerSocket getServer() throws Exception {

KeyStore ks = KeyStore.getInstance("JKS");

ks.load(new FileInputStream(keystore), keystorepass);

KeyManagerFactory kmf =

KeyManagerFactory.getInstance("SunX509");

kmf.init(ks, keypassword);

SSLContext sslcontext =

SSLContext.getInstance("SSLv3");

sslcontext.init(kmf.getKeyManagers(), null, null);

ServerSocketFactory ssf =

sslcontext.getServerSocketFactory();

SSLServerSocket serversocket = (SSLServerSocket)

ssf.createServerSocket(HTTPS_PORT);

return serversocket;

}

// multi-threading -- create a new connection

// for each request

public void run() {

ServerSocket listen;

try {

listen = getServer();

while(true) {

Socket client = listen.accept();

ProcessConnection cc = new

ProcessConnection(client);

}

} catch(Exception e) {

System.out.println("Exception: "+e.getMessage());

}

}

// main program

public static void main(String argv[]) throws Exception {

HttpsServer https = new HttpsServer();

https.run();

}

}

这几行:

String keystore = ".keystore";

char keystorepass[] = "hellothere".toCharArray();

char keypassword[] = "hiagain".toCharArray();

指定了 keystore 的名字、密码和密匙密码。直接在代码中写出密码文本是个糟糕的主意,不过我们可以在运行服务器的时候在命令行指定密码。

getServer 方法中的其它 JSSE 代码:

·         它访问 serverkeys keystoreJSK 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 请求的网页浏览器。

 上页 [ 1 2 ] 下页

 

 

(网页编辑:编程浪子

 

 

 

 

 

 

 

检索

 

 

 

 

小调查

 

    您对下面三个选题中的哪个最感兴趣?您希望计算机世界编辑部在信息化报道方面对哪些问题进行调查采访?

IT资产管理
联网审计
某水泥厂如何把ERP和自控系统成功嫁接在一起
沃尔玛信息化

 

 

 

 

关于我们 | 客服热线 | 广告服务 | 法律声明 | 投稿指南 | 联系方式
Copyright(C) ccw.com.cn,All rights reserved
中国计算机世界出版服务公司版权所有
ICP 010182

 

 

 

阅读全文
0 0

相关文章推荐

img
取 消
img