Spring-WS Wss4jSecurityInterceptor 不解密收到的消息

Spring-WS Wss4jSecurityInterceptor doesn't decrypt received message

我的案例如下:

  1. 我使用 CXF 编写了 SOAP 服务
  2. 我写了两个 SOAP 客户端 - 一个使用 CXF,另一个使用 Spring-WS
  3. 我设置了 WSSecurity:"Timestamp Signature Encrypt" 双方(客户端/服务器)的动作
  4. CXF 客户端工作正常,但Spring-WS 无法解密响应。

服务器端在与 CXF-client 和 Spring-client 交互时正常(它正在正确解密、验证签名、处理请求、签名、加密并最终发送响应)。

代码:

CXF 客户端:


<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:jaxws="http://cxf.apache.org/jaxws" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://cxf.apache.org/jaxws http://cxf.apache.org/schemas/jaxws.xsd">

<bean id="inbound-logging" class="org.apache.cxf.interceptor.LoggingInInterceptor" />
<bean id="outbound-logging" class="org.apache.cxf.interceptor.LoggingOutInterceptor" />

<jaxws:client id="helloClient" serviceClass="com.example.HelloWorld"
    address="http://localhost:8282/HelloWorld">
    <jaxws:inInterceptors>
        <ref bean="inbound-logging" />
        <ref bean="inbound-security" />
    </jaxws:inInterceptors>
    <jaxws:outInterceptors>
        <ref bean="outbound-logging" />
        <ref bean="outbound-security" />
    </jaxws:outInterceptors>
</jaxws:client>

<bean class="org.apache.cxf.ws.security.wss4j.WSS4JOutInterceptor" id="outbound-security">
    <constructor-arg>
        <map>
            <entry key="action" value="Timestamp Signature Encrypt"/>
            <entry key="user" value="client"/>              
            <entry key="signaturePropFile" value="config/client-crypto.properties"/>
            <entry key="encryptionPropFile" value="config/client-crypto.properties"/>
            <entry key="signatureKeyIdentifier" value="DirectReference"/>
            <entry key="encryptionUser" value="server"/>
            <entry key="passwordCallbackClass" value="org.mydomain.ClientPasswordCallback"/>
            <entry key="signatureParts" value="{Element}{http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd}Timestamp;{Element}{http://schemas.xmlsoap.org/soap/envelope/}Body"/>
            <entry key="encryptionParts" value="{Element}{http://www.w3.org/2000/09/xmldsig#}Signature;{Content}{http://schemas.xmlsoap.org/soap/envelope/}Body"/>
            <entry key="encryptionSymAlgorithm" value="http://www.w3.org/2001/04/xmlenc#tripledes-cbc"/>
        </map>
    </constructor-arg>
</bean>

<bean class="org.apache.cxf.ws.security.wss4j.WSS4JInInterceptor" id="inbound-security">
    <constructor-arg>
        <map>
            <entry key="action" value="Timestamp Signature Encrypt"/>
            <entry key="signaturePropFile" value="config/client-crypto.properties"/>
            <entry key="decryptionPropFile" value="config/client-crypto.properties"/>
            <entry key="passwordCallbackClass" value="org.mydomain.CustomPasswordCallback"/>
        </map>
    </constructor-arg>
</bean>


Spring-WS 客户 - wss-interceptor :


@Bean
public Wss4jSecurityInterceptor wssInterceptor(
        @Qualifier("cryptoFactoryBean") CryptoFactoryBean cryptoFactoryBean,
        @Qualifier("signValidCryptoFactoryBean") CryptoFactoryBean signValidCryptoFactoryBean) throws Exception {

    Wss4jSecurityInterceptor interceptor = new Wss4jSecurityInterceptor();

    // outgoing securement

    interceptor.setSecurementUsername("client");
    interceptor.setSecurementPassword("123456");
    interceptor.setSecurementSignatureKeyIdentifier("DirectReference");
    interceptor.setSecurementActions("Timestamp Signature Encrypt");

    interceptor.setSecurementSignatureCrypto(cryptoFactoryBean.getObject());
    interceptor.setSecurementSignatureParts(
            "{Element}{http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd}Timestamp;" +
            "{Element}{http://schemas.xmlsoap.org/soap/envelope/}Body"
    );

    interceptor.setSecurementEncryptionCrypto(cryptoFactoryBean.getObject());
    interceptor.setSecurementEncryptionUser("server");
    interceptor.setSecurementEncryptionSymAlgorithm("http://www.w3.org/2001/04/xmlenc#tripledes-cbc");
    interceptor.setSecurementEncryptionParts(
            "{Element}{http://www.w3.org/2000/09/xmldsig#}Signature;" +
            "{Content}{http://schemas.xmlsoap.org/soap/envelope/}Body"
    );

    // incoming validation:

    interceptor.setValidationActions("Timestamp Signature Encrypt");
    interceptor.setValidationDecryptionCrypto(cryptoFactoryBean.getObject());
    interceptor.setValidationSignatureCrypto(signValidCryptoFactoryBean.getObject());

    return interceptor;
}

日志记录级别设置为调试。这是结果:


(... - 与发送前加密数据相关的日志) 2015-08-09 19:09:45.181 DEBUG WSSecEncrypt:258 - 加密完成。 2015-08-09 19:09:45.182 DEBUG sent:620 - 发送请求 [SaajSoapMessage {http://www.w3.org/2001/04/xmlenc#}EncryptedData] 2015-08-09 19:09:45.223 DEBUG received:678 - 收到请求 [SaajSoapMessage {http://www.w3.org/2001/04/xmlenc#}EncryptedData] 的响应 [SaajSoapMessage {http://www.w3.org/2001/04/xmlenc#}EncryptedData] 2015-08-09 19:09:45.224 DEBUG Wss4jSecurityInterceptor:562 - 验证消息 [SaajSoapMessage {http://www.w3.org/2001/04/xmlenc#}EncryptedData] 与操作 [Timestamp Signature Encrypt] 2015-08-09 19:09:45.225 调试 WSSecurityEngine:236 - 输入 processSecurityHeader() 2015-08-09 19:09:45.229 警告 Wss4jSecurityInterceptor:281 - 无法验证请求:未找到 WS-Security header 2015-08-09 19:09:45.229 DEBUG Wss4jSecurityInterceptor:288 - 不存在异常解析器,创建基本 soap 错误 线程 "main" org.springframework.oxm.UnmarshallingFailureException 中的异常:JAXB 解组异常;嵌套异常是 javax.xml.bind.UnmarshalException: 意外元素 (uri:"http://schemas.xmlsoap.org/soap/envelope/", local:"Fault"). Expected elements are <{}helloRequest>,<{http://example.com}helloResponse> 在 org.springframework.oxm.jaxb.Jaxb2Marshaller.convertJaxbException(Jaxb2Marshaller.java:809) 在 org.springframework.oxm.jaxb.Jaxb2Marshaller.unmarshal(Jaxb2Marshaller.java:730) 在 org.springframework.ws.support.MarshallingUtils.unmarshal(MarshallingUtils.java:62) 在 org.springframework.ws.client.core.WebServiceTemplate$3.extractData(WebServiceTemplate.java:407) 在 org.springframework.ws.client.core.WebServiceTemplate.doSendAndReceive(WebServiceTemplate.java:596) 在 org.springframework.ws.client.core.WebServiceTemplate.sendAndReceive(WebServiceTemplate.java:537) 在 org.springframework.ws.client.core.WebServiceTemplate.marshalSendAndReceive(WebServiceTemplate.java:384) 在 org.springframework.ws.client.core.WebServiceTemplate.marshalSendAndReceive(WebServiceTemplate.java:374) 在 client.Main.main(Main.java:39) 在 sun.reflect.NativeMethodAccessorImpl.invoke0(本机方法) 在 sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) 在 sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) 在 java.lang.reflect.Method.invoke(Method.java:497) 在 com.intellij.rt.execution.application.AppMain.main(AppMain.java:134) 原因:javax.xml.bind.UnmarshalException: 意外元素 (uri:"http://schemas.xmlsoap.org/soap/envelope/", local:"Fault"). Expected elements are <{}helloRequest>,<{http://example.com}helloResponse> 在 com.sun.xml.internal.bind.v2.runtime.unmarshaller.UnmarshallingContext.handleEvent(UnmarshallingContext.java:726) 在 com.sun.xml.internal.bind.v2.runtime.unmarshaller.Loader.reportError(Loader.java:247) 在 com.sun.xml.internal.bind.v2.runtime.unmarshaller.Loader.reportError(Loader.java:242) 在 com.sun.xml.internal.bind.v2.runtime.unmarshaller.Loader.reportUnexpectedChildElement(Loader.java:109) 在 com.sun.xml.internal.bind.v2.runtime.unmarshaller.UnmarshallingContext$DefaultRootLoader.childElement(UnmarshallingContext.java:1131) 在 com.sun.xml.internal.bind.v2.runtime.unmarshaller.UnmarshallingContext._startElement(UnmarshallingContext.java:556) 在 com.sun.xml.internal.bind.v2.runtime.unmarshaller.UnmarshallingContext.startElement(UnmarshallingContext.java:538) 在 com.sun.xml.internal.bind.v2.runtime.unmarshaller.InterningXmlVisitor.startElement(InterningXmlVisitor.java:60) 在 com.sun.xml.internal.bind.v2.runtime.unmarshaller.SAXConnector.startElement(SAXConnector.java:153) 在 com.sun.xml.internal.bind.unmarshaller.DOMScanner.visit(DOMScanner.java:229) 在 com.sun.xml.internal.bind.unmarshaller.DOMScanner.scan(DOMScanner.java:112) 在 com.sun.xml.internal.bind.v2.runtime.unmarshaller.UnmarshallerImpl.unmarshal0(UnmarshallerImpl.java:354) 在 com.sun.xml.internal.bind.v2.runtime.unmarshaller.UnmarshallerImpl.unmarshal(UnmarshallerImpl.java:337) 在 javax.xml.bind.helpers.AbstractUnmarshallerImpl.unmarshal(摘要UnmarshallerImpl.java:127) 在 org.springframework.oxm.jaxb.Jaxb2Marshaller.unmarshal(Jaxb2Marshaller.java:726) ... 还有 12 个


看起来 Spring 正在尝试处理响应而不提前解密它。


我找到了解决问题的方法。我没有设置密码回调,所以 wss4j-interceptor 无法解密响应。这是解决问题的代码:

KeyStoreCallbackHandler keyStoreCallbackHandler = new KeyStoreCallbackHandler();
keyStoreCallbackHandler.setPrivateKeyPassword("123456");
interceptor.setValidationCallbackHandler(keyStoreCallbackHandler);

您收到对方的 soap 故障。 Soap 故障消息未加密,这就是 Wss4jSecurityInterceptor 抱怨 ws-security header.

的原因
2015-08-09 19:09:45.229 WARN  Wss4jSecurityInterceptor:281 - 
**Could not validate request: No WS-Security header found**

消息的处理似乎还在继续,而它通常应该停止。 JAXB 编组器抱怨 (uri:"http://schemas.xmlsoap.org/soap/envelope/", local:"Fault") 不是来自上下文。

2015-08-09 19:09:45.229 DEBUG Wss4jSecurityInterceptor:288 - 
No exception resolver present, creating basic soap fault
Exception in thread "main"
 org.springframework.oxm.UnmarshallingFailureException: 
JAXB unmarshalling exception; nested exception is
javax.xml.bind.UnmarshalException: unexpected element
 (uri:"http://schemas.xmlsoap.org/soap/envelope/", local:"Fault"). 
Expected elements are <{}helloRequest>,<{http://example.com}helloResponse>

我会推荐以下内容:

  1. 记录soap故障以确定对方需要什么
  2. 每当您收到 soap 故障时,您应该停止处理请求

没有为拦截器定义 KeyStoreCallbackHandler - 确实如此。适当的代码在初始 post.