I am calling a WCF service from a WCF client. The WCF client includes the MustUnderstand header attribute set to 1. Here's a typical SOAP request:
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" xmlns:u="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"> <s:Header> <o:Security s:mustUnderstand="1" xmlns:o="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"> <u:Timestamp u:Id="_0"> <u:Created>2010-08-23T20:48:52.680Z</u:Created> <u:Expires>2010-08-23T20:53:52.680Z</u:Expires> </u:Timestamp> <o:UsernameToken u:Id="uuid-72ea0c0a-43aa-43b2-bed7-c2da13624105-1"> <o:Username>blablabla</o:Username> <o:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">blablabla</o:Password> </o:UsernameToken> </o:Security> </s:Header> <s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> <HeartbeatRequest xmlns="http://removed"> <DateTime xmlns="">8/23/2010 4:48:51 PM</DateTime> <Message xmlns="">123</Message> </HeartbeatRequest> </s:Body>
Now, here is the answer that I will return to.
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <soapenv:Header> <Misunderstood qname="o:Security" xmlns="http://www.w3.org/2002/06/soap-faults" xmlns:o="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" /> </soapenv:Header> <soapenv:Body> <soapenv:Fault xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"> <faultcode>soapenv:MustUnderstand</faultcode> <faultstring>WSWS3173E: Error: Did not understand "MustUnderstand" header(s):{http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd}Security</faultstring> </soapenv:Fault> </soapenv:Body>
Please note that the MustUnderstand part is not understood.
The owner of this service indicated that they allow elements that have a WSSE namespace prefix but are not actually in the XSD, and do some other processing that would prevent them from accepting MustUnderstand = "1", so I need to find a way to send messages using MustUnderstand = "0".
I tried to change this in MessageContract for the proxy client using the MessageHeader attribute, but that did not help.
Then I implemented a special client message inspector. I created classes for MSDN for a custom behavior extension element and IEndpointBehavior, this is trivial, but here for completeness:
public class ExClientBehavior : IEndpointBehavior { #region IEndpointBehavior Members public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters) { // no op } public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime) { ExInspector inspector = new ExInspector(); clientRuntime.MessageInspectors.Add(inspector); } public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher) { // no op } public void Validate(ServiceEndpoint endpoint) { // no op } #endregion } public class ExClientBehaviorExtensionElement : BehaviorExtensionElement { public override Type BehaviorType { get { return typeof(ExClientBehavior); } } protected override object CreateBehavior() { return new ExClientBehavior(); } }
and now the actual inspector:
public class ExInspector : IClientMessageInspector { #region IClientMessageInspector Members public void AfterReceiveReply(ref Message reply, object correlationState) { // no op return; } public object BeforeSendRequest(ref Message request, IClientChannel channel) { MessageBuffer buffer = request.CreateBufferedCopy(int.MaxValue); Message newMessage = buffer.CreateMessage(); newMessage.Headers.RemoveAt(0); newMessage.Headers.Add(MessageHeader.CreateHeader ( request.Headers[0].Name, request.Headers[0].Namespace, string.Empty, false, string.Empty, request.Headers[0].Relay ) ); request = newMessage; return null; } #endregion }
As you can see, I create a new request using a buffered copy and then remove the security header (there is only one header) and add a new one with MustUnderstand set to false (why am I doing this? MessageHeader.MustUnderstand is read-only). I set a breakpoint in this method, and indeed, a new header is added, newMessage will be returned to the request, and both newMessage.Headers [0] .MustUnderstand and request.Headers [0] .MustUnderstand are false at the end of this method.
However, the message sent to the service still includes MustUnderstand = "1" in the header !!!!!
Here's the app.config, which includes the behavior above:
<configuration> <system.serviceModel> <bindings> <basicHttpBinding> <binding name="WebServiceSOAP" closeTimeout="00:01:00" openTimeout="00:01:00" receiveTimeout="00:10:00" sendTimeout="00:01:00" allowCookies="false" bypassProxyOnLocal="false" hostNameComparisonMode="StrongWildcard" maxBufferSize="65536" maxBufferPoolSize="524288" maxReceivedMessageSize="65536" messageEncoding="Text" textEncoding="utf-8" transferMode="Buffered" useDefaultWebProxy="true"> <readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="16384" maxBytesPerRead="4096" maxNameTableCharCount="16384" /> <security mode="TransportWithMessageCredential"> <transport clientCredentialType="Basic" proxyCredentialType="None" realm="" /> <message clientCredentialType="UserName" algorithmSuite="Default" /> </security> </binding> </basicHttpBinding> </bindings> <client> <endpoint address="https://removed" behaviorConfiguration="ovrExClientBehavior" binding="basicHttpBinding" bindingConfiguration="WebServiceSOAP" contract="EWebService.EWebService" name="WebServiceSOAP" /> </client> <extensions> <behaviorExtensions> <add name="exClientBehavior" type="ExMessageInspector.ExClientBehaviorExtensionElement, ExMessageInspector, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"/> </behaviorExtensions> </extensions> <behaviors> <endpointBehaviors> <behavior name="ovrExClientBehavior"> <exClientBehavior /> </behavior> </endpointBehaviors> </behaviors> </system.serviceModel>
So my question is: is it possible to change MustUnderstand to an outgoing message, as described above, or in a similar way? Or does he forcibly return to the truth later in the pipeline after the inspector replaces the security header?
Note. The service owner says that they only know about one other organization that uses this service in .NET, and that this consumer had to substantially throw out WCF and WSE and create SOAP messages - and process the responses - from scratch, probably using POX POST or some of them. We would prefer to avoid this, since we need to call several operations in the service.
In addition, we need to keep the body and properties of the message intact.
Any help would be really appreciated!