与Web服务相互验证

问题描述:

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

首先,有没有任何资源可以帮助我呢?我寻找了相当一段时间,什么都没发现。任何人都可以给我如何去做这个提示?其次,我认为我最大的障碍是我缺乏对如何处理证书的理解。我该如何协商接受服务器密钥并将自己的密钥呈现给服务器?这是用Java编写的。

一个简单的配方在this blog entry中给出。

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

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

necessary properties是:

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

这些设置成为进程所有SSL连接的默认设置。如果你想更好的控制,你必须建立你自己的SSLContext。您的webservice运行时是否可能取决于您选择的运行时间。

对于SSL(又名双向SSL)在浏览器之外的相互认证,你需要......嗯,其实,让我们看看你需要什么单向SSL第一:

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

服务器密钥库包含服务器(可能是自签名)证书和私钥。该存储由服务器用于签署消息并向客户端返回凭证。

客户端信任库包含服务器的(自签名)证书(从服务器密钥库提取到独立证书中,没有服务器私钥)。如果证书未由信任的CA签名,那么您已在与JRE捆绑在一起的truststore中拥有证书。这一步可以创建一个信任链。

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

要实现双向SSL,你需要这个设置的“对称”,所以我们需要添加:

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

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

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

一些资源,帮助您生成所有这些东西,并实现最终的解决方案:

+3

虽然你已经解释了相互认证的概念相当不错,链接是不是非常有帮助的。自2006年以来,Java Web服务安全性发生了一些变化! :) – Catchwa 2010-06-29 04:54:50

我花了很长在这个时候,但我终于找到了一个实例盟友的作品。它基于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设置为服务器,将密钥库设置为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服务属性。勾选安全服务框并选择相互证书安全性作为安全机制。点击Configure ...按钮,并勾选Encrypt Signature框。现在取消Use Use 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()); 
} 

复制web服务-api.jar文件到您的Java \ JDK1.6 \ JRE \ LIB \ endorsed目录。 右键单击Web服务引用并选择编辑Web服务属性。将密钥库位置设置为client1.jks,并将别名设置为client1。将信任库位置设置为client1.jks,并将别名设置为server

希望你现在可以运行你的客户,你应该看到像这样的输出: [email protected], CN=Bob Smith, OU=Something, O=SomethingElse, L=AnyTown, ST=AnyState, C=US