安全性是当前程序设计工作中的一个重要方面。由于应用程序在网络上呈分散分布,因而每个部分都需要保护自己的基础框架部分,而这在以前是内置到主应用程序中的。随着这些情况的变化,作为基础的 JavaTM 2 平台发行版也进行了不断演变,并在 J2SE 1.4 发行版 中并入了越来越多的功能,从而提供了一个可以执行移动代码的安全环境。
在最新的 1.4 发行版中,您会发现在安全体系结构中实现了许多增强功能。以前单独提供的几个功能现已成为核心 API 集中的一部分。其中包括通过 Java Cryptography Extension (JCE) 支持加密和解密,通过 Java Secure Socket Extension (JSSE) 实现的对安全套接字层 (SSL) 和传输层安全性 (TLS) 协议的支持,以及通过使用 Java Authentication and Authorization Service (JAAS) 实现的对基于用户的身份鉴定和访问控制的支持。除了包含这些以前的可选软件包以外,您还会发现新增了通过使用 Java Certification Path API实现的对生成和检验证书链的支持以及对 Java GSS-API 和 JAAS 下的 Kerberos V5 机制的支持。此外,还改进了安全策略管理工具 policytool,并添加了对动态载入安全策略的支持。
以下各节将介绍并深入检查所有这些功能。
Java Cryptography Extension
在 Java Cryptography Extension (JCE) 库中,您会发现对加密、解密、密钥协议、信息鉴定代码 (MAC)以及其它加密法服务的支持。由于某些国家/地区的进口控制限制,Java 2 SDK 1.4 发行版附带的JCE 辖区政策文件允许使用强大但有限的加密法。而在其它国家/地区(占大多数),则可以使用这些文件的"完全"版本,从而充分利用其强大的加密法而不受任何限制。您可以下载此版本,并使用此不受限制的版本替换随 Java 2 SDK 1.4 发行版提供的字符串加密法版本。
JCE 库可以将无格式文本转换为密文或反向转换,在 javax.crypto 软件包中可以找到该库。这里的密钥类是 Cipher,它决定了用于加密和解密的加密算法。借助 CipherOutputStream 和 CipherInputStream 类所提供的方便,这些 I/O 过滤流可以完成加密或解密任务。当您向流中写入或从中读取时,适当的流便传递给了构造函数,然后它们便可以完成各自的任务。您唯一需要关心的是密钥的管理和使用什么样的 Cipher 算法。
为演示 JCE API,以下程序将对一系列对象进行加密,然后再解密。该示例使用数据加密标准 (DES) 作为加密算法。有关所支持的算法的完整列表,请参阅 Java Cryptography Extension (JCE) Reference Guide。
代码样例 1:通过加密和解密一系列对象演示 JCE API
import java.io.*;
import javax.crypto.*;
import javax.crypto.spec.*;
import java.security.*;
import java.security.spec.*;
import java.util.*;
public class EncryptTest {
public static void main(String args[]) {
File desFile = new File("out.des");
// 创建要加密的数据
Map map = new TreeMap(System.getProperties());
int number = map.size();
try {
// 创建密钥
KeyGenerator kg = KeyGenerator.getInstance("DES");
SecretKey secretKey = kg.generateKey();
// 创建密码
Cipher desCipher =
Cipher.getInstance("DES/ECB/PKCS5Padding");
desCipher.init(Cipher.ENCRYPT_MODE, secretKey);
// 创建流
FileOutputStream fos = new FileOutputStream(desFile);
BufferedOutputStream bos = new BufferedOutputStream(fos);
CipherOutputStream cos = new CipherOutputStream(bos,
desCipher);
ObjectOutputStream oos = new ObjectOutputStream(cos);
// 写对象
oos.writeObject(map);
oos.writeInt(number);
oos.flush();
oos.close();
// 更改密码模式
desCipher.init(Cipher.DECRYPT_MODE, secretKey);
// 创建流
FileInputStream fis = new FileInputStream(desFile);
BufferedInputStream bis = new BufferedInputStream(fis);
CipherInputStream cis = new CipherInputStream(bis,
desCipher);
ObjectInputStream ois = new ObjectInputStream(cis);
// 读对象
Map map2 = (Map)ois.readObject();
int number2 = ois.readInt();
ois.close();
// 将读回的内容与原始内容相比较
if (map.equals(map2) && (map.size() == number2)) {
System.out.println("Everything read back out okay.");
} else {
System.out.println("Problems during
encryption/decryption process.");
}
} catch (NoSuchPaddingException e) {
System.err.println("Padding problem: " + e);
} catch (NoSuchAlgorithmException e) {
System.err.println("Invalid algorithm: " + e);
} catch (InvalidKeyException e) {
System.err.println("Invalid key: " + e);
} catch (IOException e) {
System.err.println("I/O Problem: " + e);
} catch (ClassNotFoundException e) {
System.err.println("Class loading Problem: " + e);
} finally {
if (desFile.exists()) {
desFile.delete();
}
}
}
}
Java Secure Socket Extension
接下来将介绍 Java Secure Socket Extension (JSSE) 库。从中您会发现对使用安全套接字层 (SSL) 和传输层安全性 (TLS) 协议进行通信的支持。与对特定的本地数据结构执行操作的 JCE 不同,JSEE 使用了另一种提取方法,从而对网络套接字通信应用加密/解密技术。它添加了服务器鉴定、信息完整性和可选的客户机鉴定。大多数人把 SSL 和 TLS 视为安全的 HTTP 协议,并称为 HTTPS。
JSSE 库位于 javax.net 和 javax.net.ssl 软件包中。要使用 SSL 进行套接字通信,您必须借助一个辅助机制, 而不是从其构造函数中创建 Socket。首先从 SSLSocketFactory 获取一个 SocketFactory,然后再从该工厂创建 Socket。当建立了安全连接后,只需写入套接字或从中读取,即可对数据进行加密或解密。在此机制之上可以建立其它高层协议,如 HTTPS。
SSL(亦即 HTTPS)允许在客户机和服务器之间交换加密的通信。当 SSL 客户机开始与 SSL 服务器的对话后,服务器会向客户机发送回一个 X.509 证书要求进行鉴定。然后客户机将检查该证书的有效性。假设服务器通过检验,客户机将预先生成一个安全密钥,然后使用证书中的服务器的公共密钥对其进行加密,并将加密后的密钥发送回服务器。根据此事先掌握的密钥,客户机和服务器将为会话生成一个主密钥。经过一番基本的握手程序后,加密交换便可以开始了。
JSSE 库对您隐藏了 SSL 协议的这些内部工作。您只需象对待普通套接字一样使用从工厂创建的 Socket。
以下示例演示了如何使用 JSSE 库。它使用 SSL 读取了一个 Web 页。即使发送到服务器的 GET 命令说明要使用 HTTP协议,但因为 Socket 是从 SSLSocketFactory 创建的,所以仍然使用了 SSL 与服务器进行通信,以发送页面请求并接收响应。
代码样例 2:通过从端口 443(即 HTTPS 端口)读取 Web 页,演示如何使用 JSSE 库
import java.io.*;
import java.net.*;
import javax.net.*;
import javax.net.ssl.*;
public class JSSE {
static final int HTTPS_PORT = 443;
public static void main(String args[]) {
String hostname;
// 获得主机名或使用 VeriSign
if (args.length == 0) {
hostname = "www.verisign.com";
} else {
hostname = args[0];
}
try {
// 获得套接字工厂
SocketFactory factory = SSLSocketFactory.getDefault();
// 从工厂获得套接字
Socket s = factory.createSocket(hostname, HTTPS_PORT);
// 发送请求
OutputStream os = s.getOutputStream();
PrintWriter pw = new PrintWriter(os);
// 设置命令
String command = "GET / HTTP/1.0\r\n\r\n";
pw.print(command);
pw.flush();
// 获得响应
InputStream is = s.getInputStream();
InputStreamReader isr = new InputStreamReader(is);
BufferedReader br = new BufferedReader(isr);
String line;
while ((line = br.readLine()) != null) {
System.out.println(line);
}
pw.close();
br.close();
s.close();
} catch (IOException e) {
System.err.println("Error reading: " + e);
}
}
}
在创建服务器而不是客户机的情况下,您会从 SSLServerSocketFactory 获得一个 ServerSocket。
还请注意,J2SE 1.4 发行版中的 JSSE 库与 JSSE 1.0.2 发行版中的 JSSE 库不同。例如,com.sun.net.ssl 软件包现在是 javax.net.ssl 软件包,并且新增和修改了某些接口和类。在 Sun 的 J2SE 1.4 发行版中,Wrapper