22

目前,只要客户端使用 Web 浏览器访问网站,我就已经成功实现了相互身份验证安全性,因为浏览器会为您处理所有证书交换。现在我需要创建一个安全接口,用户可以使用该接口通过 HTTPS 访问 Web 服务,使用服务器所需的相互身份验证。

首先,是否有任何人知道的资源可以帮助我解决这个问题?我已经找了很长时间了,什么也没找到。任何人都可以给我关于如何解决这个问题的任何其他提示?

其次,我认为我最大的障碍是我对如何处理证书缺乏了解。如何协商接受服务器的密钥并将我自己的密钥提供给服务器?这是在 Java 中。

4

4 回答 4

14

我花了很长时间,但我终于找到了一个真正有效的例子。它是基于 Glassfish 和 Netbeans 的,但我想如果你使用它,你可以让它在其他环境(例如 Eclipse 和 Tomcat)中工作。

http://java.sun.com/webservices/reference/tutorials/wsit/doc/WSIT_Security9.html#wp162511

我发现的问题是当您想使用自己的证书时,而不是预装 glassfish 的证书。

注意:我不是安全专家。不要将其部署到生产环境!

为此,我使用了 NetBeans 6.9、JDK 1.6、GlassFish 3.0.1 和 OpenSSL v1.0(我使用的是非官方的 Win32 二进制文件)

# Create the CA
mkdir ca server client
cd ca
openssl req -new -x509 -days 3650 -extensions v3_ca -keyout ca.key -out ca.pem
echo 02 > serial.txt
cd ..

# Creating the Server Keystore

openssl req -days 3650 -newkey rsa:1024 -keyout server/server.key -out server/server.req
openssl x509 -extensions usr_cert -extfile C:\testbed\OpenSSL-Win32\bin\openssl.cfg -CA ca/ca.pem -CAkey ca/ca.key -CAserial ca/serial.txt -req -in server/server.req -out server/server.crt
openssl pkcs12 -export -inkey server/server.key -in server/server.crt -out server/server.p12 -name server
keytool -importkeystore -destkeystore server/server.jks -deststoretype jks -srckeystore server/server.p12 -srcstoretype pkcs12
keytool -exportcert -alias server -keystore server/server.jks -file server/server.cer

# Create the Client Keystore

openssl req -days 3650 -newkey rsa:1024 -keyout client/client1.key -out client/client1.req
openssl x509 -extensions usr_cert -extfile C:\testbed\OpenSSL-Win32\bin\openssl.cfg -CA ca/ca.pem -CAkey ca/ca.key -CAserial ca/serial.txt -req -in client/client1.req -out client/client1.crt
openssl pkcs12 -export -inkey client/client1.key -in client/client1.crt -out client/client1.p12 -name client1
keytool -importkeystore -destkeystore client/client1.jks -deststoretype jks -srckeystore client/client1.p12 -srcstoretype pkcs12
keytool -exportcert -alias client1 -keystore client/client1.jks -file client/client1.cer

# Import public keys and certificates into each others keystores

keytool -import -noprompt -trustcacerts -alias client1 -file client/client1.cer -keystore server/server.jks
keytool -import -noprompt -trustcacerts -alias server -file server/server.cer -keystore client/client1.jks
keytool -import -noprompt -trustcacerts -alias my_ca -file ca/ca.pem -keystore server/server.jks
keytool -import -noprompt -trustcacerts -alias my_ca -file ca/ca.pem -keystore client/client1.jks
keytool -import -noprompt -trustcacerts -alias my_ca -file ca/ca.pem -keystore "C:\Program Files\glassfish-3.0.1\glassfish\domains\domain1\config\cacerts.jks"
keytool -import -noprompt -trustcacerts -alias my_ca -file ca/ca.pem -keystore "C:\Program Files\Java\jdk1.6\jre\lib\security\cacerts"
move "C:\Program Files\glassfish-3.0.1\glassfish\domains\domain1\config\keystore.jks" "C:\Program Files\glassfish-3.0.1\glassfish\domains\domain1\config\keystore.jks.backup"
copy server\server.jks "C:\Program Files\glassfish-3.0.1\glassfish\domains\domain1\config\keystore.jks"

在 GlassFish 管理控制台中,在您的 http-listener 上启用安全性,勾选 SSL3、TLS 和客户端身份验证框,将证书昵称设置为服务器,将密钥库设置为 config\keystore.jks,将信任库设置为 config\keystore.jks ,信任算法到 PKIX 并将最大证书长度保留为 5。

在 NetBeans 中,创建一个新的 Web 应用程序项目。在其中,创建一个新的 Web 服务。

我的 Web 服务代码如下所示:

@WebService()
public class ListProducts {

  @Resource WebServiceContext context;

  @WebMethod(operationName = "listProducts")
  public String listProducts() {
    return context.getUserPrincipal().toString();
  }

}

右键单击 Web 服务并选择编辑 Web 服务属性。勾选 Secure Service 框并选择 Mutual Certificates Security 作为安全机制。单击配置...按钮并勾选加密签名框。现在取消选中 Use Development Defaults 框,然后单击 Keystore 按钮。设置 server.jks 密钥库的位置并选择server别名。对 Truststore 配置执行相同的操作(尽管您不必在此处选择别名)。

将 client1.p12 客户端证书导入浏览器。将您的 Web 服务部署到 Glassfish。在浏览器中打开您的 Web 服务并通过 HTTPS 浏览到已部署的 WSDL。下载 WSDL 和任何其他模式。将任何引用的模式重命名为本地副本,以便在您使用 WSDL2Java 时,NetBeans 不会使用任何远程资源。(本段是因为您已将 WSDL 限制为具有批准证书的客户端,但 NetBeans 无法远程获取它,因为它无权访问相关证书)。

创建一个新的 Java 项目。创建一个新的 Web 服务客户端。出现提示时,将 NetBeans 指向您保存的 WSDL 文件。导入 METRO2.0 库文件 ( C:\Program Files\Netbeans 6.9\enterprise\modules\ext\metr\webservices-*.jar)。我的代码如下所示:

public static void main(String[] args) {
  System.getProperties().put("javax.net.ssl.keyStore", "C:\\NetBeansProjects\\security-04\\ssl\\client\\client1.jks");
  System.getProperties().put("javax.net.ssl.keyStorePassword", "changeit");
  System.getProperties().put("javax.net.ssl.trustStore", "C:\\NetBeansProjects\\security-04\\ssl\\client\\client1.jks");
  System.getProperties().put("javax.net.ssl.trustStorePassword", "changeit");
  System.out.println(new ListProductsService().getListProductsPort().listProducts());
}

将 webservices-api.jar 复制到您的 Java\jdk1.6\jre\lib\endorsed 目录中。右键单击 Web 服务引用并选择编辑 Web 服务属性。将密钥库位置设置为 client1.jks 并将别名设置为client1. 将信任库位置设置为 client1.jks 并将别名设置为server.

希望您现在可以运行您的客户端,您应该会看到如下输出: EMAILADDRESS=bob@anonymous.org, CN=Bob Smith, OU=Something, O=SomethingElse, L=AnyTown, ST=AnyState, C=US

于 2010-05-27T04:43:04.860 回答
13

对于在浏览器外部使用 SSL(又名双向 SSL)的相互身份验证,您需要... 好吧,实际上,让我们先看看您需要单向 SSL 什么:

  1. 服务器密钥库
  2. 客户端信任库

服务器密钥库包含服务器的(可能是自签名的)证书和私钥。服务器使用此存储对消息进行签名并将凭据返回给客户端。

客户端信任库包含服务器的(自签名)证书(从服务器密钥库中提取到独立证书中,没有服务器私钥)。如果证书不是由受信任的 CA 签署的,那么这是必需的,您在与 JRE 捆绑的信任库中已有证书。此步骤允许创建信任链。

有了这个,您可以实现单向 SSL(传统用例)。

要实现双向 SSL,您需要将此设置设置为“对称”,因此我们需要添加:

  1. 客户端密钥库
  2. 服务器信任库

客户端密钥库包含客户端的(可能是自签名的)证书和私钥。客户端使用此存储的目的与服务器密钥存储相同,即在 TLS 相互身份验证握手期间将客户端凭据发送到服务器。

服务器信任库包含客户端(自签名)独立证书(从客户端密钥库中提取到独立证书中,没有客户端私钥)。出于与前面提到的完全相同的原因,这是必需的。

一些资源可帮助您生成所有这些内容并实施最终解决方案:

于 2010-02-11T00:25:22.243 回答
6

如果 Web 服务库使用标准java.net.URL类作为 HTTP 客户端,您可以设置一些系统属性,双向身份验证将由内置的 HTTPS 支持处理。

必要的属性是:

  • javax.net.ssl.trustStore:包含根 CA 证书
  • javax.net.ssl.keyStore:包含客户端证书和私钥
  • javax.net.ssl.keyStorePassword: 保护客户私钥的密码

这些设置成为进程所有 SSL 连接的默认设置。如果您想要更好的控制,您必须设置自己的SSLContext. 您的 Web 服务运行时是否可行取决于您选择的运行时。

于 2010-02-10T23:01:47.607 回答
1

此博客条目中提供了一个简单的方法。

但我认为真正的答案可能取决于您用于实现客户端 HTTP 交互的 Java API。例如,看起来您会使用 JAX-RPC做一些不同的事情。

于 2010-02-10T22:58:52.220 回答