网格安全基础设施(GSI)是 Java™ 通用安全服务(Generic Security Service,GSS-API)的实现。GSS 用来在互相通信的应用程序之间安全地交换消息,它在各种底层安全机制(例如 Kerberos)之上提供了对安全服务的一致访问。在本文中,您将学习如何使用 GSI/GSS-API 扩展和代理证书构建自己的客户机-服务器应用程序。这是网格中间件所使用的基本身份验证机制。
通过模仿进行身份验证
代理证书 是一种模仿证书。模仿(Impersonation)是一种安全技术,允许实体 A 对另外一个实体(实体 B)进行授权,让 B 可以对其他实体进行验证,就仿佛 B 是实体 A 一样。换而言之,B 模仿了 A。
因此为什么我们会希望使用模仿技术呢?这是因为它有助于解决分布式网络中关于身份验证的两个重要问题:
单点登录 ?? 为什么单点登录这么重要呢?假设一个用户需要在多个资源上运行某个进程,使用单点登录,用户只需要一次身份验证,而不用对每个资源都进行身份验证。
委托 ?? 之所以需要使用委托是因为进程需要代表用户进行身份验证。因此,必须向其委托所需的权力。
为便于解释,假设一个用户在两个主机之间启动一个远程执行服务。这个服务需要代表用户在资源上使用单点登录进行身份验证,然后必须将自己的权力委托给这两个主机,这样它们就可以相互进行身份验证了。代理证书让您可以实现这种任务。
相互进行身份验证
当双方都具有数字证书并且都信任对这些证书进行签名的证书权威(CA)时,这两个实体可以执行相互身份验证从而彼此证明它们就是自己声称的那个实体。信任签名 CA 实际上意味着它们必须有一些 CA 证书的拷贝(包含公钥),并且信任这些证书确实来自这些 CA。两个实体(A 和 B)之间的相互身份验证过程如下所示:
A 建立一个到 B 的连接。要启动身份验证过程, A 将自己的证书发送给 B。这个证书声明了 A 的身份、公钥和用来证明该证书的 CA。
B 通过检查 CA 的数字签名来确保该证书是有效的,从而确保该 CA 对这个证书进行了签名,并且这个证书并没有被篡改过,从而确保这个证书是有效的。(B 必须信任对 A 的证书进行签名的 CA)。
B 通过生成一条随机消息并将其发送给 A,请求 A 对其进行加密,从而确保 A 确实是证书所标识的人。 A 使用自己的私钥对这条消息进行加密,并将结果发回给 B。B 使用 A 的公钥对消息进行解密。如果解密后的消息与原来的随机消息相同,那么 B 就可确定 A 就是它声称的那个身份(B 信任 A 的身份)。
第 3 个步骤中相同的操作必须按照相反的顺序再执行一次。
现在,A 和 B 就已经相互进行了身份验证。
代理证书
代理证书包括一个新证书,其中包括一个新公钥以及一个新私钥。这个新证书包含一个修改过的所有者的身份,它是由所有者进行签名的,而不是由 CA 进行签名的。代理证书具有以下特性:
有限的生命期 ?? 它具有一个特定的截止时间,之后这个代理就不再有效了。
未加密的私钥 ?? 由于代理很长时间内都不是有效的,因此其私钥就不需要像所有者的私钥一样安全地进行保存。因此,可以使用一个未加密的私钥将其存储在临时空间中,只要存放私匙的文件权限阻止任何人查看该私匙。
一旦创建之后,用户就可以使用代理证书和私钥来进行身份验证,而不需要输入密码。
代理证书是对 Globus 项目所创建的 Transport Later Security(TLS)协议的扩展。Globus 结合 Global Grid Forum 一起工作,使得代理成为 TLS 的一个标准扩展,这样 GSI 代理就可以与其他 TLS 软件一起使用了。
构建自己的启用 GSI 的客户机服务器
我们在这里要构建的应用程序名字是 Client 和 Server。它们是使用 Java 编程语言编写的,需要 Commodity Grid (CoG) Kit 所提供的 GSI 实现和支持库。构建自己的启用 GSI 的应用程序非常简单。Client 和 Server 的骨架可以分解为如下内容:
读取命令行参数
在客户机和服务器之间建立 socket 连接来传输数据
加载代理证书
建立安全上下文
如果需要,安全地交换消息
清除工作
读取命令行参数
Client 和 Server 的 main 方法需要做的第一件事也是最简单的事是读取命令行参数。
Client 需要使用两个参数:主机名和要连接的端口。
清单 1. Client 的主机名和端口
// load arguments
if (args.length < 2)
{
System.err.println("Usage: java {options} Client "
+ " {hostName} {port}");
System.exit(-1);
}
String hostName = args[0];
int port = Integer.parseInt(args[1]);
Server 需要一个参数:监听连接使用的端口号。
清单 2. Server 端口号
// read the command-line arguments
if (args.length != 1) {
System.err.println("Usage: java {options} Server {localPort}");
System.exit(-1);
}
int localPort = Integer.parseInt(args[0]);
建立 socket 连接
Java GSS-API 为创建和解释标记(不透明的字节数据)提供了方法。这些标记包含双方之间安全交换的消息,不过实际进行标记传输的方法取决于交换双方。对于我们的目的来说,在客户机和服务器之间建立了一个 socket 连接,并使用从 socket 流和安全上下文中构造的流来交换数据。
Client 需要建立一个到 Server 的 socket 连接,并从中提取输入/输出使用的流,如下所示:
清单 3. Client 建立一个到 Server 的 socket 连接
Socket socket = new Socket(hostName, port);
DataInputStream inStream =
new DataInputStream(socket.getInputStream());
DataOutputStream outStream =
new DataOutputStream(socket.getOutputStream());
System.out.println("Client: Connected to server "
+ socket.getInetAddress());
服务器应用程序创建了一个 ServerSocket 来监听这个端口,使用下面的方式给出参数:
ServerSocket ss = new ServerSocket(localPort);
ServerSocket 然后可以等待并接受一个来自客户机的连接,然后对 I/O 流进行初始化,以便以后与客户机进行数据交换。
[1] [2] [3] 下一页