How to change WCF MustUnderstand message header using ClientInspector

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 &quot;MustUnderstand&quot; 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!

+6
c # soap web-services wcf
source share
1 answer

I wonder why interoperability standards exist if suppliers do not follow them. If the client inspector is not working, you can try to implement your own message encoder and change the header there.

Edit:

The question is why you are sending user credentials for authentication, if at the same time you announce that the service does not need to understand the header with the credentials = do not need to use them. Do you really need them?

There are other approaches depending on your requirements. Do you need a timestamp? Is the timestamp checked on the server? Do you have one user for all calls or do you have different users between calls?

If you do not need a timestamp, or if the timestamp is not verified, and you only have one username and password, the easiest way is to not use the TranportWithMessageCredential security mode. Instead, use a clean transport and place the header description inside the client endpoint configuration, for example:

 <client> <endpoint address="https://removed" binding="basicHttpBinding" bindingConfiguration="WebServiceSOPA" contract="EWebService.EWebService" name="WebServiceSOAP"> <headers> <wsse:Security s:mustUnderstand="0" xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" xmlns:s="http://schemas.xmlsoap.org/soap/envelope/"> <wsse:UsernameToken wsu:Id="SecurityToken-3f7f983f-66ce-480d-bce6-170632d33f92" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"> <wsse:Username>User</wsse:Username> <wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">Pwd123</wsse:Password> </wsse:UsernameToken> </wsse:Security> </headers> </endpoint> </client> 

If you have multiple usernames or you need a real timestamp with the actual data, you can use the same approach, but instead of a static configuration, you can create your own header in the code and avoid WCF security. This can be done using the message inspector.

+5
source share

All Articles