Kristian Kristensen’s Blog


Removing the SOAP mustUnderstand attribute from a WCF message

Posted in Code,Microsoft,WCF by Kristian Kristensen on the January 10th, 2011

I’ve been working on an integration to an endpoint that’s been under development. Furthermore the service is special in that the endpoint I hit acts as a forwarding gateway to an internal endpoint. This had implications for the security configuration of the service. A new release meant that my configured UsernameToken WS-Security binding stopped working. I suspect it was denied by the gateway but I’m not entirely sure. Either way the error message suggested that it had something to do with a SOAP mustUnderstand attribute not being understood. This blog post details how you can apply a fix that’ll allow you to get through.

The SOAP mustUnderstand attribute says that the recipient must understand and obey the semantics of the SOAP header to which it is applied, or fail processing the message.

Investigating the messages going across the wire I found that the mustUnderstand attribute was only applied to the WS-Security header. It makes sense that you want the security configuration to be obeyed by the recipient, otherwise there wouldn’t be any point in sending it over. The service though failed to process it. I had an equivalent call working through SoapUI, the reason being that SoapUI can be configured to omit the mustUnderstand attribute. Hence I knew that if I could remove the mustUnderstand attribute from my WCF call I’d have end-to-end connection.
However, removing that is not so easy! Out of the box WCF doesn’t allow you to remove this by simply flipping a switch. It makes a lot of sense, but doesn’t really help me much.

WCF is very extensible. Actually to the point where it’s so extensible that it can be tough to figure out where to hook in. To remove the mustUnderstand attribute from the Security header you need to hook into the pipeline where the header is about to be applied or just after it’s been applied. One place to do that is with a Custom Message Encoder. Microsoft has a sample Message Encoder that adds character set and media type to a text encoder. Based on this sample I wrote some code that looks at the message before it’s being written on the wire and alters the XML.
You want to hook into the WriteMessage method of the Message Encoder. In here you can read the message into XLINQ and manipulate it.

public override ArraySegment<byte> WriteMessage(Message message, int maxMessageSize, BufferManager bufferManager, int messageOffset)
{
 MemoryStream stream = new MemoryStream();
XmlWriter writer = XmlWriter.Create(stream, this.writerSettings);
message.WriteMessage(writer);
writer.Close();
stream.Position = 0;

//Interesting part
XNamespace soapEnvelope = "http://schemas.xmlsoap.org/soap/envelope/";

Namespace sec = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd";

var securityHeader = xmlMessage.Descendants(sec + "Security");
var attribute = securityHeader.Attributes(soapEnvelope + "mustUnderstand").First();
if (attribute != null)
    attribute.Remove();

//End Interesting part

stream.Position = 0;

xmlMessage.Save(stream);

byte[] messageBytes = stream.GetBuffer();
int messageLength = (int)stream.Position;
stream.Close();

int totalLength = messageLength + messageOffset;
byte[] totalBytes = bufferManager.TakeBuffer(totalLength);
Array.Copy(messageBytes, 0, totalBytes, messageOffset, messageLength);

ArraySegment<byte> byteArray = new ArraySegment<byte>(totalBytes, messageOffset, messageLength);
return byteArray;

I’ve included the full method for reference, but the main thing is between the “Interesting part” comments. First we define some namespaces that we’ll use to locate the Security header and mustUnderstand attribute. Then we extract the Security header element and look for the XML mustUnderstand attribute identified in the SOAP envelope namespace. We remove the attribute. The full code base then continues by writing the modified XML message to the stream being returned by WCF.

If you’re using the sample from Microsoft there’s an error in the app.config that adds the Custom Message Encoder. The full element needs to look like this:

<extensions>
      <bindingElementExtensions>
        <add name="customTextMessageEncoding" type="Microsoft.Samples.CustomTextMessageEncoder.CustomTextMessageEncodingElement, TestApplication"/>
      </bindingElementExtensions>
    </extensions>

In the sample documentation the class in the type-attribute is wrongly put down as CustomTextMessageEncodingBindingSection and it needs to be CustomTextMessageEncodingElement as shown above.

You exchange the Text Message Encoder with this new Custom Encoder and it’ll remove the mustUnderstand attribute from the SOAP header. Setting this up on a custom binding is seen below:

<customTextMessageEncoding messageVersion="Soap11" mediaType="text/xml" />

instead of

<textMessageEncoding messageVersion="Soap11" />

With this in place my WCF client could now talk to the service endpoint again. Later the gateway service was fixed so it didn’t break when a Security-header with mustUnderstand was sent to it. However this example serves to show that you can modify the message going across the wire in a lot of ways. You have access to the full XML, and hence can do whatever is required. Be careful though when dealing with the Security aspects of WCF, especially when you have timestamps, signatures, and encryption involved. It only takes a single character to invalidate a signature, and hence you want to be careful where and what you change.

  • If you like my writing you should subscribe to my RSS feed.

    Setting UserNameToken Client Credentials on a Send Activity in WF4

    Posted in Code,Microsoft,WCF,WF4 by Kristian Kristensen on the January 4th, 2011

    With .NET 4.0 we also got a new release of Windows Workflow Foundation. It’s a complete rewrite of the Workflow framework that was part of .NET 3.0/3.5. One of the strong points of the new release is in the integration between WF and WCF. Part of this is being able to call web services easily from inside Workflows. This is accomplished using the built in Send Activity. Depending on your integration scenario you might need to have some pretty elaborate service configurations and hence bindings. Once you’ve configured your WCF binding, which is what WF uses to actually call the web service, you should be able to go.

    I had to call a service that required a UserNameToken configured. Setting this up in WCF is easy. Configuring WF to call using the WCF binding is moderately easy as well. But specifying the UserName ClientCredentials is not so easy. In regular code calling WCF you could write:

    client.ClientCredentials.UserName.UserName = USERNAME;
    client.ClientCredentials.UserName.Password = PASSWORD;
    

    Doing this in WF is not so easy since you don’t have access to the client proxy. The next thought might be to specify it in WCF configuration, however unlike other Client Credentials the Username and password is not externally configurable. Obviously because that would put the two in clear text in a configuration file. In an MSDN Forum post a developer from Micrsoft acknowledges that this feature isn’t supported in the initial release. So how do you accomplish this scenario?

    One solution is described here Setting UserName ClientCredentials in WF SendActivity. With this in place you attach to the processing pipeline in WCF and can hence set the Client Credentials as you please. Obviously you can retrieve the username and password from where ever you want. When the Send Activity does the call to the web service via WCF this custom extension will be called and you’re set to go.

  • If you like my writing you should subscribe to my RSS feed.