Spring-WS Wss4jSecurityInterceptor 不解密收到的消息
Spring-WS Wss4jSecurityInterceptor doesn't decrypt received message
我的案例如下:
- 我使用 CXF 编写了 SOAP 服务
- 我写了两个 SOAP 客户端 - 一个使用 CXF,另一个使用 Spring-WS
- 我设置了 WSSecurity:"Timestamp Signature Encrypt" 双方(客户端/服务器)的动作
- 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>
我会推荐以下内容:
- 记录soap故障以确定对方需要什么
- 每当您收到 soap 故障时,您应该停止处理请求
没有为拦截器定义 KeyStoreCallbackHandler - 确实如此。适当的代码在初始 post.
我的案例如下:
- 我使用 CXF 编写了 SOAP 服务
- 我写了两个 SOAP 客户端 - 一个使用 CXF,另一个使用 Spring-WS
- 我设置了 WSSecurity:"Timestamp Signature Encrypt" 双方(客户端/服务器)的动作
- 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>
我会推荐以下内容:
- 记录soap故障以确定对方需要什么
- 每当您收到 soap 故障时,您应该停止处理请求
没有为拦截器定义 KeyStoreCallbackHandler - 确实如此。适当的代码在初始 post.