This week a BizTalk solution I had designed encountered an exception I’ve never seen before, as below.
The issue was intermittent, and encountered while communicating with a third party web service. Said web service was based on the Java stack rather than .NET and was being hosted by an ESB unknown to me. This web service was based on the WS-I Basic Profile 1.1 so I was using a WCF-Custom adapter (mainly because I employed some custom behaviors to work around some funny behavior with this web service) employing the basicHttpBinding. The security mode was set to transportWithMessageCredential while the clientCredentialType was set to Username, so I was effectively using SSL to encrypt my message with the username and password contained within the SOAP Headers in my request.
Seeing as the target system was a Java system and the error raised by the send port was clearly a .NET type error, I immediately suspected that the WCF stack on the BizTalk server must be raising the exception rather than the web service being called upon. To prove my theory I decided to enable WCF tracing by adding the below within the system.serviceModel section of my BTSNTSVC64.exe.config file (the send host instance was 64-bit) and restarted my send host instance.
<diagnostics> <messageLogging logEntireMessage="true" logMalformedMessages="true" logMessagesAtServiceLevel="true" logMessagesAtTransportLevel="true" maxMessagesToLog="300000" maxSizeOfMessageToLog="200000" /> </diagnostics> </system.serviceModel> <system.diagnostics> <sources> <source name ="System.ServiceModel" switchValue="Verbose"> <listeners> <add name="xml" /> </listeners> </source> <source name ="System.ServiceModel.MessageLogging" switchValue="Verbose, ActivityTracing"> <listeners> <add name="xml" /> </listeners> </source> <source name ="System.Runtime.Serialization" switchValue="Verbose"> <listeners> <add name="xml" /> </listeners> </source> </sources> <sharedListeners> <add name="xml" type="System.Diagnostics.XmlWriterTraceListener" traceOutputOptions="LogicalOperationStack" initializeData="C:\log\WCFTrace.svclog" /> </sharedListeners> <trace autoflush="true" /> </system.diagnostics>
With tracing enabled I played another few messages through BizTalk until I had a few success and failure scenarios. Studying the trace outputs using the Microsoft Service Trace Viewer tool I found that I was always getting a response from the service in question, but in the successful response messages I had SOAP security headers included, whereas in the failure messages (which were otherwise normal response messages from the service) I didn’t.
Below is a snippet of the SOAP headers for a success message.
<SOAP-ENV:Header> <wsse:Security xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" SOAP-ENV:mustUnderstand="1"> <wsu:Timestamp xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" wsu:Id="Timestamp-96477"> <wsu:Created>2015-02-25T02:22:38.760Z</wsu:Created> <wsu:Expires>2015-02-25T02:27:38.760Z</wsu:Expires> </wsu:Timestamp> </wsse:Security> </SOAP-ENV:Header>
Whereas I found that the SOAP headers were completely empty in the case of the failure messages. The problem was clearly on the service side, and we told the third party in charge of the service that they needed to resolve this. However I decided that I wanted to dive a bit deeper and find out whether there was any way to get WCF/BizTalk to support the missing SOAP security headers.
The first port of call was to attempt to replicate the problem. I managed to create a stub for the service with the exact same binding using .NET on my development VM using all the same bindings and authentication modes. I struggled to find a way to programmatically remove the headers from my message. I suspected that I could write a Message Inspector WCF behavior to do so, but I was too lazy🙂
Fiddler came to my rescue. I enabled the logging of HTTPS traffic, and toggled Fiddler to automatically apply breakpoints on responses. The beauty of fiddler is that once it applied a breakpoint on the response, I was able to decrypt it (since it was encrypted using HTTPS), tamper with it (to remove the security SOAP headers) and then submit my tampered response back to BizTalk. Voila, I had now found a way to reproduce my problem.
The next step was to find a way to work around the problem. I found that there was no way to do so with the basicHttpBinding that I was using (and I’d bet that this wouldn’t be available with the other out of the box bindings either). I changed my send port to use a customBinding instead, replicating my basicHttpBinding configuration by employing textMessageEncoding (very importantly setting the messageVersion attribute to Soap11 since I had to abide by the WS-I Basic Profile 1.1), httpsTransport, and the security binding elements. I configured the security element such that the authenticationMode was set to UserNameOverTransport to mirror the UserName clientCredentialType in my original configuration. Now the magic was to set the enableUnsecuredResponse attribute to true in the security binding element.
Doing this resulted in BizTalk being able to consume the response messages, regardless whether the security headers were included in the response or not. If for whatever reason the third party in question was unable to fix the problem on their end I now had a fallback plan.
The final piece of the puzzle was trying to see if I could get my .NET based WCF service (not BizTalk) to return a response without the security SOAP headers, so that I wouldn’t have to use Fiddler to replicate this problem anymore. Armed with the knowledge about the enableUnsecuredResponse property on the security binding element I proceeded to switch my WCF Service from using basicHttpBinding to also employ a custom binding.
My original binding looked like the below.
<basicHttpBinding> <binding name="BasicHttpEndpointBinding"> <security mode="TransportWithMessageCredential"> <message clientCredentialType="UserName" /> </security> </binding> </basicHttpBinding>
While my updated binding looked like the below.
<customBinding> <binding name="customBasicHttpBinding"> <security authenticationMode="UserNameOverTransport" enableUnsecuredResponse="true"/> <textMessageEncoding messageVersion="Soap11" /> <httpsTransport /> </binding> </customBinding>
Since I had included the enableUnsecuredResponse attribute in my security binding element, my WCF service was now returning responses without the security SOAP headers. The same would apply for a BizTalk receive location using a custom binding as well. Would you ever want to employ this trick? I’d say the answer is a resounding no since this enables non-standard behavior, unless for whatever reason you are backed into a corner on a project where you’re integrating with a fiddly system that will not accept the security SOAP headers (I can’t imagine this being the case but then again I wouldn’t be overly surprised).