与 Web 服务的相互身份验证

Posted

技术标签:

【中文标题】与 Web 服务的相互身份验证【英文标题】:Mutual-authentication with web services 【发布时间】:2011-01-15 12:05:56 【问题描述】:

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

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

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

【问题讨论】:

【参考方案1】:

我在这方面花了很长时间,但我终于找到了一个真正有效的例子。它是基于 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

【讨论】:

【参考方案2】:

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

    服务器密钥库 客户端信任库

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

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

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

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

    客户端密钥库 服务器信任库

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

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

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

The Fifteen Minute Guide to Mutual Authentication Two-Way SSL in Weblogic for Developers Using JAX-WS-Based Web Services with SSL Mutual Authentication for Web Services: A Live Example

【讨论】:

虽然您已经很好地解释了相互身份验证的概念,但这些链接根本没有多大帮助。自 2006 年以来,Java Web 服务安全性发生了相当大的变化! :)【参考方案3】:

this blog entry 给出了一个简单的食谱。

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

【讨论】:

【参考方案4】:

如果网络服务库使用标准的java.net.URL类作为HTTP客户端,你可以设置一些system properties,双向认证将由内置的HTTPS支持处理。

necessary properties 是:

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

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

【讨论】:

以上是关于与 Web 服务的相互身份验证的主要内容,如果未能解决你的问题,请参考以下文章

与 Tomcat 7 的相互身份验证

相互 SSL - 多少身份验证就足够了?

基于相互证书的身份验证能否与基于 SSL 的基本身份验证共存,用于同一应用程序中的不同路径?

OpenSSL 的相互身份验证始终成功

如何使 Django REST JWT 身份验证与多个 Web 服务器一起扩展?

JBoss 相互证书身份验证在 SSL 握手时失败