与Web服务的双向身份验证

22

目前,只要客户端使用浏览器访问网站,我已经成功实现了相互认证(Mutual Authentication)安全措施,因为浏览器会自动处理所有证书交换。现在,我需要创建一个安全接口,使用户可以通过HTTPS访问Web服务,并使用服务器所需的相互认证。

首先,有没有任何资源可以帮助我完成这个任务?我找了很长时间也没找到什么。还有其他建议吗?

其次,我认为我最大的障碍是我不太了解如何处理证书。我该如何协商接受服务器的密钥并向服务器呈现我的密钥?这是用Java实现的。

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监听器上启用安全性,并选中SSL3、TLS和客户端身份验证框,将证书NickName设置为server,密钥库设置为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服务属性。勾选安全服务框并选择双向证书安全作为安全机制。点击配置按钮并勾选加密签名框。现在取消勾选使用开发默认值框,然后点击密钥库按钮。设置您的server.jks密钥库的位置,并选择server别名。对于信任库配置也要做同样的操作(但这里不必选择别名)。

将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

13

要在浏览器外进行SSL的相互认证(也称为双向SSL),您需要... 先了解一下单向SSL需要什么:

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

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

客户端信任库包含服务器的(自签名的)证书(从服务器密钥库中提取为独立证书,不含服务器私钥)。如果该证书未由受信任的CA签名,并且没有在JRE捆绑的信任库中找到已签名的证书,则需要此步骤。此步骤允许创建信任链。

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

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

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

客户端密钥库包含客户端的(可能是自签名的)证书和私钥。客户端使用此存储与服务器进行TLS相互认证握手期间发送客户端凭据,与服务器密钥库的用途相同。

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

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

  • 使用 JAX-WS 实现带 SSL 的 Web 服务
  • Web 服务的双向身份验证:现场示例

  • 3
    虽然您已经相当好地解释了相互认证的概念,但这些链接并没有太大帮助。自2006年以来,Java Web服务安全性已经发生了相当大的变化! :) - Catchwa

    6
    如果Web服务库使用标准的java.net.URL类作为HTTP客户端,您可以设置一些系统属性,内置的HTTPS支持将处理双向身份验证。 必要属性包括:
    • javax.net.ssl.trustStore:包含根CA证书
    • javax.net.ssl.keyStore:包含客户端证书和私钥
    • javax.net.ssl.keyStorePassword:保护客户端私钥的密码
    这些设置成为该进程所有SSL连接的默认值。如果您需要更精细的控制,您需要设置自己的SSLContext。是否可以在您选择的webservice运行时进行设置取决于您选择的运行时。

    1

    这篇博客文章中提供了一个简单的方法(链接)

    但我认为真正的答案可能取决于您使用哪些Java API来实现客户端HTTP交互。例如,使用JAX-RPC似乎需要稍微不同的方法


    网页内容由stack overflow 提供, 点击上面的
    可以查看英文原文,
    原文链接