从 Apache CXF 访问 WCF Web 服务(MTOM + WS-Security)
我已经为这个问题苦苦挣扎了很长一段时间,尽管到处寻找,但仍无法解决。
场景详细信息(代码、配置和其他内容发布在最后):
- WCF Web 服务 (.NET 4),公开两个端点:一个不安全,一个使用消息安全和用户名身份验证(使用成员资格基础结构)进行保护 - 无传输安全。
- 两个端点都使用MTOM编码; WSDL 引用的所有类都用 MessageContract 属性标记,并且用 MessageBodyMember 属性标记成员。
- WCF 绑定上的配置将“assetSecurityContext”和“negotiateServiceCredential”设置为 false。
- WCF 服务使用自签名证书进行保护以进行加密。
- 尝试使用 Apache CXF 2.5.1 从 Java 客户端进行访问。我已将证书设置到可从类路径访问的 KeyStore 中。
结果:
- 从 .NET 客户端访问 Web 服务工作正常
- 从 Apache CXF 访问不安全的端点,无论是否使用 MTOM 编码,都工作正常。
- 从 Apache CXF 访问安全端点(没有 MTOM)效果很好。 CXF追踪表明加解密过程正常进行。
- 一旦在WCF中激活MTOM,问题就出现了。我们能够确定在 WCF 端,消息的接收、消息的处理以及响应的生成/加密正常发生。
- 问题发生在CXF接收消息的过程中。我们认为 CXF 在尝试解密消息之前没有正确反序列化 MTOM 附件;因此,解密逻辑发现消息在响应中仍然有一个要解密的元素,但发现一个空元素要解密并失败。我们尝试添加 RECEIVE 阶段拦截器来初始化附件,但无济于事(尝试在多部分响应中找到边界标记失败)。
- 使用 Fiddler,我们发现响应看起来格式良好,并带有一个引用
CODE/CONFIG/STUFF 的附件:
Web.Config(为清楚起见而缩短):
<system.serviceModel>
<!-- Services -->
<services>
<service name="TestServices.Services.TestService" behaviorConfiguration="TestService.Basic">
<!-- Plain (unsecured) endpoint -->
<endpoint address="" binding="wsHttpBinding" bindingConfiguration="TestService.Basic" name="TestService.Basic"
contract="TestServices.Interfaces.ITestService" bindingNamespace="http://searchtechnologies.com/cpa/wcfinterop"/>
<!-- Secure endpoint -->
<endpoint address="/secure" binding="wsHttpBinding" bindingConfiguration="TestService.Secure" name="TestService.Secure"
contract="TestServices.Interfaces.ITestService" bindingNamespace="http://searchtechnologies.com/cpa/wcfinterop"/>
</service>
</services>
<!-- Bindings -->
<bindings>
<wsHttpBinding>
<binding name="TestService.Basic" messageEncoding="Mtom">
<security mode="None">
<message clientCredentialType="None"/>
<transport clientCredentialType="None"/>
</security>
</binding>
<binding name="TestService.Secure" messageEncoding="Mtom">
<security mode="Message">
<message clientCredentialType="UserName" establishSecurityContext="false" negotiateServiceCredential="false"/>
<transport clientCredentialType="None"/>
</security>
</binding>
</wsHttpBinding>
</bindings>
<!-- Behaviors -->
<behaviors>
<serviceBehaviors>
<behavior name="TestService.Basic">
<serviceMetadata httpGetEnabled="true"/>
<serviceDebug includeExceptionDetailInFaults="true"/>
<serviceCredentials>
<serviceCertificate storeName="My" storeLocation="LocalMachine" x509FindType="FindBySubjectName" findValue="equiros-PC2.search.local"/>
<userNameAuthentication userNamePasswordValidationMode="MembershipProvider" membershipProviderName="SqlProvider"/>
</serviceCredentials>
</behavior>
</serviceBehaviors>
</behaviors>
Java 客户端代码(为清楚起见省略了部分):
private static void secureTest() {
try {
logger.debug("Secure test starting. Instatiating service");
// service instantiation
TestService service = new TestService();
ITestService port = service.getTestServiceSecure();
// request creation
BasicRequest request = objectFactory.createBasicRequest();
request.setStringData("String data");
request.setIntegerData("Integer data");
// setup logging
setupInterceptors(port);
// setup security & binding
setupBinding(port);
// service call
logger.debug("Attempting service call");
BasicResponse response = port.simpleOperation(request);
logger.debug("Call success!!!");
logger.debug(String.format("Response data: str: %1$s; int: %2$s",
response.getStringData(), response.getIntegerData()));
} catch (Exception e) {
logger.error("Error during service invocation", e);
}
}
private static void setupInterceptors(ITestService port) {
Client proxy = JaxWsClientProxy.getClient(port);
proxy.getInInterceptors().add(new LoggingInInterceptor());
proxy.getOutInterceptors().add(new LoggingOutInterceptor());
}
private static void setupBinding(ITestService port) {
javax.xml.ws.BindingProvider bp = (javax.xml.ws.BindingProvider) port;
SOAPBinding binding = (SOAPBinding) bp.getBinding();
binding.setMTOMEnabled(true);
Map<String, Object> context = bp.getRequestContext();
context.put("ws-security.username", "systemadmin");
context.put("ws-security.password", "Pass@word1");
context.put("ws-security.encryption.properties",
"clientKeystore.properties");
context.put("ws-security.encryption.username", "serviceKey");
}
错误跟踪(仅解密阶段) :
[main] DEBUG org.apache.ws.security.components.crypto.CryptoFactory - Using Crypto Engine [class org.apache.ws.security.components.crypto.Merlin]
[main] DEBUG org.apache.ws.security.util.Loader - Trying to find [clientKeystore.jks] using sun.misc.Launcher$AppClassLoader@2a340e class loader.
[main] DEBUG org.apache.ws.security.components.crypto.Merlin - The KeyStore clientKeystore.jks of type jks has been loaded
[main] DEBUG org.apache.ws.security.processor.TimestampProcessor - Found Timestamp list element
[main] DEBUG org.apache.ws.security.message.token.Timestamp - Current time: 2012-02-07T22:52:22.852Z
[main] DEBUG org.apache.ws.security.message.token.Timestamp - Timestamp created: 2012-02-07T22:52:22.641Z
[main] DEBUG org.apache.ws.security.message.token.Timestamp - Timestamp expires: 2012-02-07T22:57:22.641Z
[main] DEBUG org.apache.ws.security.message.token.Timestamp - Validation of Timestamp: Everything is ok
[main] DEBUG org.apache.ws.security.message.token.DerivedKeyToken - DerivedKeyToken: created : element constructor
[main] DEBUG org.apache.ws.security.message.token.DerivedKeyToken - DerivedKeyToken: created : element constructor
[main] DEBUG org.apache.ws.security.processor.ReferenceListProcessor - Found reference list element
[main] DEBUG org.apache.ws.security.processor.ReferenceListProcessor - Found data reference: _3
[main] DEBUG org.apache.ws.security.processor.X509Util - Sym Enc Algo: http://www.w3.org/2001/04/xmlenc#aes256-cbc
[main] DEBUG org.apache.xml.security.encryption.XMLCipher - Getting XMLCipher with transformation
[main] DEBUG org.apache.xml.security.encryption.XMLCipher - Constructing XMLCipher...
[main] DEBUG org.apache.xml.security.algorithms.JCEMapper - Request for URI http://www.w3.org/2001/04/xmlenc#aes256-cbc
[main] DEBUG org.apache.xml.security.encryption.XMLCipher - cipher._algorithm = AES/CBC/ISO10126Padding
[main] DEBUG org.apache.xml.security.encryption.XMLCipher - Initializing XMLCipher...
[main] DEBUG org.apache.xml.security.encryption.XMLCipher - opmode = DECRYPT_MODE
[main] DEBUG org.apache.xml.security.encryption.XMLCipher - Processing source element...
[main] DEBUG org.apache.xml.security.encryption.XMLCipher - Decrypting element...
[main] DEBUG org.apache.xml.security.encryption.XMLCipher - Decrypting to ByteArray...
[main] DEBUG org.apache.xml.security.utils.ElementProxy - setElement("KeyInfo", "null")
[main] DEBUG org.apache.xml.security.encryption.XMLCipher - Encrypted octets:
x1eLqhngbuRTq2XJIkcTdzyu1UFb4eV0kwno04/w4yW0HiY6RyYa7OHqniV63aaxgZPxm0NOK2ZUgjggtkM0O9myJ6ZJOFxCLmqREjQMD+mFW+WuTSEZ5cgc3SFule3MmryqoStNLsmzM8t5yaT3drF1ctT7DJQnV6W858WwpD+Dw+WYmO0RaUlgsfbTnWiBvCZ8yyCzvgmZTMGr8y9LXnwaw+FsspReuMpcIOsqU9LE5u5uW5ZJglgn5cv/8XWikD3TwNzqL+7qAVN8R6WnXgUmb1DuX5lx4cyxlwcLnkfOQKbGrwGvKJUY47ohAgKH
[main] DEBUG org.apache.xml.security.algorithms.JCEMapper - Request for URI http://www.w3.org/2001/04/xmlenc#aes256-cbc
[main] DEBUG org.apache.xml.security.encryption.XMLCipher - JCE Algorithm = AES/CBC/ISO10126Padding
[main] DEBUG org.apache.xml.security.encryption.XMLCipher - Decrypted octets:
<BasicResponse xmlns="http://searchtechnologies.com/cpa/wcfinterop/data"><DateTimeData>0001-01-02T00:00:00</DateTimeData><IntegerData>Integer data</IntegerData><StringData>String data</StringData></BasicResponse>
[main] DEBUG org.apache.ws.security.processor.ReferenceListProcessor - Found data reference: _6
[main] DEBUG org.apache.ws.security.processor.X509Util - Sym Enc Algo: http://www.w3.org/2001/04/xmlenc#aes256-cbc
[main] DEBUG org.apache.xml.security.encryption.XMLCipher - Getting XMLCipher with transformation
[main] DEBUG org.apache.xml.security.encryption.XMLCipher - Constructing XMLCipher...
[main] DEBUG org.apache.xml.security.algorithms.JCEMapper - Request for URI http://www.w3.org/2001/04/xmlenc#aes256-cbc
[main] DEBUG org.apache.xml.security.encryption.XMLCipher - cipher._algorithm = AES/CBC/ISO10126Padding
[main] DEBUG org.apache.xml.security.encryption.XMLCipher - Initializing XMLCipher...
[main] DEBUG org.apache.xml.security.encryption.XMLCipher - opmode = DECRYPT_MODE
[main] DEBUG org.apache.xml.security.encryption.XMLCipher - Processing source element...
[main] DEBUG org.apache.xml.security.encryption.XMLCipher - Decrypting element...
[main] DEBUG org.apache.xml.security.encryption.XMLCipher - Decrypting to ByteArray...
[main] DEBUG org.apache.xml.security.utils.ElementProxy - setElement("KeyInfo", "null")
[main] DEBUG org.apache.xml.security.encryption.XMLCipher - Encrypted octets:
[main] DEBUG org.apache.xml.security.algorithms.JCEMapper - Request for URI http://www.w3.org/2001/04/xmlenc#aes256-cbc
[main] DEBUG org.apache.xml.security.encryption.XMLCipher - JCE Algorithm = AES/CBC/ISO10126Padding
Feb 07, 2012 4:52:22 PM org.apache.cxf.ws.security.wss4j.WSS4JInInterceptor handleMessage
WARNING:
org.apache.ws.security.WSSecurityException: The signature or decryption was invalid
at org.apache.ws.security.processor.ReferenceListProcessor.decryptEncryptedData(ReferenceListProcessor.java:298)
at org.apache.ws.security.processor.ReferenceListProcessor.decryptDataRefEmbedded(ReferenceListProcessor.java:159)
at org.apache.ws.security.processor.ReferenceListProcessor.handleReferenceList(ReferenceListProcessor.java:93)
at org.apache.ws.security.processor.ReferenceListProcessor.handleToken(ReferenceListProcessor.java:60)
at org.apache.ws.security.WSSecurityEngine.processSecurityHeader(WSSecurityEngine.java:396)
at org.apache.cxf.ws.security.wss4j.WSS4JInInterceptor.handleMessage(WSS4JInInterceptor.java:249)
at org.apache.cxf.ws.security.wss4j.WSS4JInInterceptor.handleMessage(WSS4JInInterceptor.java:85)
at org.apache.cxf.phase.PhaseInterceptorChain.doIntercept(PhaseInterceptorChain.java:263)
at org.apache.cxf.endpoint.ClientImpl.onMessage(ClientImpl.java:799)
at org.apache.cxf.transport.http.HTTPConduit$WrappedOutputStream.handleResponseInternal(HTTPConduit.java:1627)
at org.apache.cxf.transport.http.HTTPConduit$WrappedOutputStream.handleResponse(HTTPConduit.java:1494)
at org.apache.cxf.transport.http.HTTPConduit$WrappedOutputStream.close(HTTPConduit.java:1402)
at org.apache.cxf.io.CacheAndWriteOutputStream.postClose(CacheAndWriteOutputStream.java:47)
at org.apache.cxf.io.CachedOutputStream.close(CachedOutputStream.java:195)
at org.apache.cxf.transport.AbstractConduit.close(AbstractConduit.java:56)
at org.apache.cxf.transport.http.HTTPConduit.close(HTTPConduit.java:649)
at org.apache.cxf.interceptor.MessageSenderInterceptor$MessageSenderEndingInterceptor.handleMessage(MessageSenderInterceptor.java:62)
at org.apache.cxf.phase.PhaseInterceptorChain.doIntercept(PhaseInterceptorChain.java:263)
at org.apache.cxf.endpoint.ClientImpl.doInvoke(ClientImpl.java:533)
at org.apache.cxf.endpoint.ClientImpl.invoke(ClientImpl.java:463)
at org.apache.cxf.endpoint.ClientImpl.invoke(ClientImpl.java:366)
at org.apache.cxf.endpoint.ClientImpl.invoke(ClientImpl.java:319)
at org.apache.cxf.frontend.ClientProxy.invokeSync(ClientProxy.java:88)
at org.apache.cxf.jaxws.JaxWsClientProxy.invoke(JaxWsClientProxy.java:134)
at $Proxy33.simpleOperation(Unknown Source)
at com.searchtechnologies.wcfinterop.TestServiceClient.secureTest(TestServiceClient.java:74)
at com.searchtechnologies.wcfinterop.TestServiceClient.main(TestServiceClient.java:32)
Caused by: java.lang.ArrayIndexOutOfBoundsException
at java.lang.System.arraycopy(Native Method)
at org.apache.xml.security.encryption.XMLCipher.decryptToByteArray(Unknown Source)
at org.apache.xml.security.encryption.XMLCipher.decryptElement(Unknown Source)
at org.apache.xml.security.encryption.XMLCipher.doFinal(Unknown Source)
at org.apache.ws.security.processor.ReferenceListProcessor.decryptEncryptedData(ReferenceListProcessor.java:296)
... 26 more
Feb 07, 2012 4:52:22 PM org.apache.cxf.phase.PhaseInterceptorChain doDefaultLogging
WARNING: Interceptor for {http://searchtechnologies.com/cpa/wcfinterop}TestService#{http://searchtechnologies.com/cpa/wcfinterop}SimpleOperation has thrown exception, unwinding now
org.apache.cxf.binding.soap.SoapFault: The signature or decryption was invalid
at org.apache.cxf.ws.security.wss4j.WSS4JInInterceptor.createSoapFault(WSS4JInInterceptor.java:643)
at org.apache.cxf.ws.security.wss4j.WSS4JInInterceptor.handleMessage(WSS4JInInterceptor.java:308)
at org.apache.cxf.ws.security.wss4j.WSS4JInInterceptor.handleMessage(WSS4JInInterceptor.java:85)
at org.apache.cxf.phase.PhaseInterceptorChain.doIntercept(PhaseInterceptorChain.java:263)
at org.apache.cxf.endpoint.ClientImpl.onMessage(ClientImpl.java:799)
at org.apache.cxf.transport.http.HTTPConduit$WrappedOutputStream.handleResponseInternal(HTTPConduit.java:1627)
at org.apache.cxf.transport.http.HTTPConduit$WrappedOutputStream.handleResponse(HTTPConduit.java:1494)
at org.apache.cxf.transport.http.HTTPConduit$WrappedOutputStream.close(HTTPConduit.java:1402)
at org.apache.cxf.io.CacheAndWriteOutputStream.postClose(CacheAndWriteOutputStream.java:47)
at org.apache.cxf.io.CachedOutputStream.close(CachedOutputStream.java:195)
at org.apache.cxf.transport.AbstractConduit.close(AbstractConduit.java:56)
at org.apache.cxf.transport.http.HTTPConduit.close(HTTPConduit.java:649)
at org.apache.cxf.interceptor.MessageSenderInterceptor$MessageSenderEndingInterceptor.handleMessage(MessageSenderInterceptor.java:62)
at org.apache.cxf.phase.PhaseInterceptorChain.doIntercept(PhaseInterceptorChain.java:263)
at org.apache.cxf.endpoint.ClientImpl.doInvoke(ClientImpl.java:533)
at org.apache.cxf.endpoint.ClientImpl.invoke(ClientImpl.java:463)
at org.apache.cxf.endpoint.ClientImpl.invoke(ClientImpl.java:366)
at org.apache.cxf.endpoint.ClientImpl.invoke(ClientImpl.java:319)
at org.apache.cxf.frontend.ClientProxy.invokeSync(ClientProxy.java:88)
at org.apache.cxf.jaxws.JaxWsClientProxy.invoke(JaxWsClientProxy.java:134)
at $Proxy33.simpleOperation(Unknown Source)
at com.searchtechnologies.wcfinterop.TestServiceClient.secureTest(TestServiceClient.java:74)
at com.searchtechnologies.wcfinterop.TestServiceClient.main(TestServiceClient.java:32)
Caused by: org.apache.ws.security.WSSecurityException: The signature or decryption was invalid
at org.apache.ws.security.processor.ReferenceListProcessor.decryptEncryptedData(ReferenceListProcessor.java:298)
at org.apache.ws.security.processor.ReferenceListProcessor.decryptDataRefEmbedded(ReferenceListProcessor.java:159)
at org.apache.ws.security.processor.ReferenceListProcessor.handleReferenceList(ReferenceListProcessor.java:93)
at org.apache.ws.security.processor.ReferenceListProcessor.handleToken(ReferenceListProcessor.java:60)
at org.apache.ws.security.WSSecurityEngine.processSecurityHeader(WSSecurityEngine.java:396)
at org.apache.cxf.ws.security.wss4j.WSS4JInInterceptor.handleMessage(WSS4JInInterceptor.java:249)
... 21 more
Caused by: java.lang.ArrayIndexOutOfBoundsException
at java.lang.System.arraycopy(Native Method)
at org.apache.xml.security.encryption.XMLCipher.decryptToByteArray(Unknown Source)
at org.apache.xml.security.encryption.XMLCipher.decryptElement(Unknown Source)
at org.apache.xml.security.encryption.XMLCipher.doFinal(Unknown Source)
at org.apache.ws.security.processor.ReferenceListProcessor.decryptEncryptedData(ReferenceListProcessor.java:296)
... 26 more
[main] ERROR com.searchtechnologies.wcfinterop.TestServiceClient - Error during service invocation
java.lang.NullPointerException
at com.sun.xml.internal.messaging.saaj.soap.impl.ElementImpl.addTextNode(ElementImpl.java:439)
at com.sun.xml.internal.messaging.saaj.soap.ver1_2.Fault1_2Impl.setFaultRole(Fault1_2Impl.java:323)
at com.sun.xml.internal.messaging.saaj.soap.ver1_2.Fault1_2Impl.setFaultActor(Fault1_2Impl.java:559)
at org.apache.cxf.jaxws.JaxWsClientProxy.createSoapFault(JaxWsClientProxy.java:219)
at org.apache.cxf.jaxws.JaxWsClientProxy.invoke(JaxWsClientProxy.java:152)
at $Proxy33.simpleOperation(Unknown Source)
at com.searchtechnologies.wcfinterop.TestServiceClient.secureTest(TestServiceClient.java:74)
at com.searchtechnologies.wcfinterop.TestServiceClient.main(TestServiceClient.java:32)
如您所见,当 CxF 尝试解码引用附件的元素时,它发现其内容为空,并且失败。
任何帮助将深切感激。
由于大小限制,无法发布 WSDL。如有需要,可应要求提供。
此致,
爱德华多·基罗斯-坎波斯
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
据我所知,当启用签名时,CXF 并不完全支持 MTOM:它不会对附件进行签名或加密。由于 WCF 会对附件进行签名或加密,因此它不起作用。
请参阅http://cxf.547215.n5.nabble.com/Signature-digest-mismatch-when-NET-supplies-MTOM-attachment-td3270961.html 和 https://issues.apache.org/jira/browse/CXF-3687。
As far as I known, CXF does not support completely MTOM when signature is enable : it doesn't sign or encrypt the attachments. As WCF do sign or encryt attachments, it won't work.
See http://cxf.547215.n5.nabble.com/Signature-digest-mismatch-when-NET-supplies-MTOM-attachment-td3270961.html and https://issues.apache.org/jira/browse/CXF-3687.