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

How do I get the senders Public Key using CXF?

我想弄清楚是否可以获取调用方法的发送方的 Public 密钥作为方法参数。 如果我有一个 CXF 发布的具有非对称安全性的 SOAP 服务,是否可以通过某种方式告诉 CXF 使调用程序的 public 密钥可用于它在调用 Web 服务时调用的方法?

我希望能够将 public 键定义为方法调用的额外参数,例如:

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

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

我也不太清楚这是否可以通过回调或拦截器来完成,但考虑到可以有多个处理线程,我不知道如何做。再次,将不胜感激。

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

SSL 协议本身应该足以做到这一点。如果您使用 HTTPS 发布 Web 服务,那么您可以将服务器配置为向客户端询问它的证书(其中将包含它的 public 密钥)。 为此,您可以检查以下 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

现在您已准备好获取 public 密钥!为此,我将假设您在 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");

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

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

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

WS-安全

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

  1. 注册拦截器:
<jaxws:inInterceptors>
    <bean class="my.beloved.MyWSInterceptor"/>
</jaxws:inInterceptors>
  1. MyWSInterceptor 拦截器编码为 AbstractSoapInterceptor 的子 class 并实现 handleMessage(SoapMessage message)方法:

public class 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

考虑到您已经为您的网络服务正确配置了两种方式的 SSL, 您可以使用拦截器实现此目的。但是 public 键不会传递给方法调用。

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 获取发件人 public 密钥。

然后在您的 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"/>