如何使用CXF获取发件人公钥?

时间:2015-02-26 09:37:15

标签: java soap cxf

我试图弄清楚是否有可能获取调用方法的发送方的公钥作为方法参数。 如果我有一个具有非对称安全性的CXF发布的SOAP服务,是否有可能告诉CXF在调用Web服务时调用它所调用的方法的调用者的公钥?

我希望能够将公钥定义为方法调用的额外参数,如下所示:

public interface Webservices{
    public ReturnVal soapMethod(SomeObject input, SomeOtherObject moreInput, PublicKey invokerPubKey)
}

返回的pubkey不必作为PublicKey对象,字节数组或任何其他我可以使用的格式返回。

我也无法弄清楚这是否可以通过回调或拦截器完成,但鉴于可以有多个处理线程,我不知道如何。再次,建议将不胜感激。

我不一定需要密钥对象本身,获取别名或任何其他唯一标识符也可以,只要它会引导我存储密钥。

2 个答案:

答案 0 :(得分:2)

SSL协议本身应该足以做到这一点。如果使用HTTPS发布Web服务,则可以将服务器配置为向客户端询问其证书(包括它的公钥)。 为此,您可以查看以下CXF configuration file。如您所见,有一部分说:

<sec:clientAuthentication want="true" required="true"/>

这告诉服务器当客户端尝试建立连接时它应该询问客户端的证书。

之后,您需要进行更多配置:

  1. 服务器应识别签署客户端证书的证书颁发机构。您可以在server's trust store

  2. 中添加CA.
  3. 客户显然应该拥有它的证书。如果它是一个java应用程序,您可以通过adding the certificate in a keystore来完成。

  4. 您还可以查看complete CXF example

    现在您已准备好获取公钥!为此,我将假设您在Java EE JAX-WS应用程序中使用CXF。

    第一步是通过添加以下字段将WebServiceContext注入到@WebService中:

    @Resource
    private WebServiceContext webServiceContext;
    

    然后你应该从WebServiceContext获取HttpServletRequest表单:

    MessageContext messageContext = webServiceContext.getMessageContext();
    HttpServletRequest request = (HttpServletRequest)
                  messageContext.get(MessageContext.SERVLET_REQUEST);
    

    之后,您应该从请求中获取证书链:

    X509Certificate[] certificates = (X509Certificate[])      
           request.getAttribute("javax.servlet.request.X509Certificate");
    

    最后,您应该从中提取公钥:

    PublicKey publicKey = certificates[0].getPublicKey();

    (客户端的证书应该是数组中的第一个)

    <强> WS-Security的

    如果您使用的是WS-Security,则可以执行以下操作:

    1. 注册拦截器:
    2. <jaxws:inInterceptors>
          <bean class="my.beloved.MyWSInterceptor"/>
      </jaxws:inInterceptors>
      
      1. MyWSInterceptor 拦截器编码为 AbstractSoapInterceptor 的子类,并实现 handleMessage(SoapMessage消息)方法:
      2. 公共类MyWSInterceptor扩展了AbstractSoapInterceptor {

        public void handleMessage(SoapMessage message) throws Fault {
            List<WSHandlerResult> results = CastUtils.cast((List<?>) message
                .get(WSHandlerConstants.RECV_RESULTS));
            for (WSHandlerResult wshr : results) {
                for (WSSecurityEngineResult wsser : wshr.getResults()) {
                    PublicKey publicKey = wsser
                        .get(WSSecurityEngineResult.TAG_PUBLIC_KEY);
                }
            }
        }
        

        }

        有关CXF中WS-Security配置的更多信息,请选中here

答案 1 :(得分:2)

考虑到您已为Web服务正确配置双向SSL, 你可以通过拦截器实现这一目标。但是公钥不会传递给方法调用。

public class TestInterceptor extends AbstractPhaseInterceptor<Message> {

public TestInterceptor() {
    super(Phase.RECEIVE);
}

public void handleMessage(Message message) throws Fault {
    TLSSessionInfo tlsSessionInfo = (TLSSessionInfo) message
            .get(TLSSessionInfo.class);
    if (tlsSessionInfo != null) {
        Certificate[] peerCerts = tlsSessionInfo.getPeerCertificates();

        for (int i = 0; i < peerCerts.length; i++) {
            X509Certificate x509certificate = (X509Certificate)peerCerts[i];
            x509certificate.getPublicKey(); //DO SOMETHING WITH PUBLIC KEY

            }
    } else {
            System.out.println(" NO x509certificate ");
    }
}

}

通过这种方式,您可以使用CXF获取发件人公钥。

然后在你的cxf-servlet.xml或其他端点映射中配置上面的拦截器如下。

 <jaxws:endpoint publish="true" id="helloWorld" implementor="demo.spring.service.HelloWorldImpl" address="/HelloWorld"  >
<jaxws:inInterceptors> <ref bean="srinathSSLInterceptor"/></jaxws:inInterceptors>
</jaxws:endpoint>
<bean id="srinathSSLInterceptor" class="TestInterceptor"/>