Search code examples
springcxfspring-wsws-security

Spring-WS Wss4jSecurityInterceptor doesn't decrypt received message


My case is as follows:

  1. I wrote SOAP service using CXF
  2. I wrote two SOAP clients - one using CXF and another using Spring-WS
  3. I set up WSSecurity : "Timestamp Signature Encrypt" action on both sides (client / server)
  4. CXF client works like charm, but Spring-WS cant decrypt response.

Server side is OK while interacting with CXF-client and Spring-client (it is properely decrypting, verifing signature, processing requests, sign, encrypt and finnaly send response).

Code:

CXF client:


<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 client - 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;
}

Logging level is set to debug. Here is the result:


(... - logs related with encrypting data befrore send ) 2015-08-09 19:09:45.181 DEBUG WSSecEncrypt:258 - Encryption complete. 2015-08-09 19:09:45.182 DEBUG sent:620 - Sent request [SaajSoapMessage {http://www.w3.org/2001/04/xmlenc#}EncryptedData] 2015-08-09 19:09:45.223 DEBUG received:678 - Received response [SaajSoapMessage {http://www.w3.org/2001/04/xmlenc#}EncryptedData] for request [SaajSoapMessage {http://www.w3.org/2001/04/xmlenc#}EncryptedData] 2015-08-09 19:09:45.224 DEBUG Wss4jSecurityInterceptor:562 - Validating message [SaajSoapMessage {http://www.w3.org/2001/04/xmlenc#}EncryptedData] with actions [Timestamp Signature Encrypt] 2015-08-09 19:09:45.225 DEBUG WSSecurityEngine:236 - enter processSecurityHeader() 2015-08-09 19:09:45.229 WARN Wss4jSecurityInterceptor:281 - Could not validate request: No WS-Security header found 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> at org.springframework.oxm.jaxb.Jaxb2Marshaller.convertJaxbException(Jaxb2Marshaller.java:809) at org.springframework.oxm.jaxb.Jaxb2Marshaller.unmarshal(Jaxb2Marshaller.java:730) at org.springframework.ws.support.MarshallingUtils.unmarshal(MarshallingUtils.java:62) at org.springframework.ws.client.core.WebServiceTemplate$3.extractData(WebServiceTemplate.java:407) at org.springframework.ws.client.core.WebServiceTemplate.doSendAndReceive(WebServiceTemplate.java:596) at org.springframework.ws.client.core.WebServiceTemplate.sendAndReceive(WebServiceTemplate.java:537) at org.springframework.ws.client.core.WebServiceTemplate.marshalSendAndReceive(WebServiceTemplate.java:384) at org.springframework.ws.client.core.WebServiceTemplate.marshalSendAndReceive(WebServiceTemplate.java:374) at client.Main.main(Main.java:39) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:497) at com.intellij.rt.execution.application.AppMain.main(AppMain.java:134) Caused by: javax.xml.bind.UnmarshalException: unexpected element (uri:"http://schemas.xmlsoap.org/soap/envelope/", local:"Fault"). Expected elements are <{}helloRequest>,<{http://example.com}helloResponse> at com.sun.xml.internal.bind.v2.runtime.unmarshaller.UnmarshallingContext.handleEvent(UnmarshallingContext.java:726) at com.sun.xml.internal.bind.v2.runtime.unmarshaller.Loader.reportError(Loader.java:247) at com.sun.xml.internal.bind.v2.runtime.unmarshaller.Loader.reportError(Loader.java:242) at com.sun.xml.internal.bind.v2.runtime.unmarshaller.Loader.reportUnexpectedChildElement(Loader.java:109) at com.sun.xml.internal.bind.v2.runtime.unmarshaller.UnmarshallingContext$DefaultRootLoader.childElement(UnmarshallingContext.java:1131) at com.sun.xml.internal.bind.v2.runtime.unmarshaller.UnmarshallingContext._startElement(UnmarshallingContext.java:556) at com.sun.xml.internal.bind.v2.runtime.unmarshaller.UnmarshallingContext.startElement(UnmarshallingContext.java:538) at com.sun.xml.internal.bind.v2.runtime.unmarshaller.InterningXmlVisitor.startElement(InterningXmlVisitor.java:60) at com.sun.xml.internal.bind.v2.runtime.unmarshaller.SAXConnector.startElement(SAXConnector.java:153) at com.sun.xml.internal.bind.unmarshaller.DOMScanner.visit(DOMScanner.java:229) at com.sun.xml.internal.bind.unmarshaller.DOMScanner.scan(DOMScanner.java:112) at com.sun.xml.internal.bind.v2.runtime.unmarshaller.UnmarshallerImpl.unmarshal0(UnmarshallerImpl.java:354) at com.sun.xml.internal.bind.v2.runtime.unmarshaller.UnmarshallerImpl.unmarshal(UnmarshallerImpl.java:337) at javax.xml.bind.helpers.AbstractUnmarshallerImpl.unmarshal(AbstractUnmarshallerImpl.java:127) at org.springframework.oxm.jaxb.Jaxb2Marshaller.unmarshal(Jaxb2Marshaller.java:726) ... 12 more


It looks like Spring is trying to process response without decrypting it earlier.


I've found solution to the problem. I didn't set password callback, so wss4j-interceptor couldn't decrypt response. Here is code that fix the issue:

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

Solution

  • KeyStoreCallbackHandler wasn't defined for the interceptor - that was the case. Appropriate code in the initial post.