Recently I was working on a BizTalk project that included a secured (SSL) SOAP connection using a WCF-Custom send port that was pointing to the partner’s endpoint. Our send port raised an interesting exception when sending a test message to our partner:
A message sent to adapter "WCF-Custom" on send port "<SEND PORT NAME>" with URI "<PARTNER’S URL>" is suspended.
Error details: System.ServiceModel.CommunicationException: An error occurred while making the HTTP request to <PARTNER’S URL>. This could be due to the fact that the server certificate is not configured properly with HTTP.SYS in the HTTPS case. This could also be caused by a mismatch of the security binding between the client and the server. ---> System.Net.WebException: The underlying connection was closed: An unexpected error occurred on a send. ---> System.IO.IOException: Received an unexpected EOF or 0 bytes from the transport stream.
After several hours of analysis (with the aid of some network sniffing tools) we found out that our partner was not accepting TLS 1.0 as the security protocol. Great, now what?
It seems that BizTalk (and I suppose all other .Net based solutions) executes the SSL handshakes in some sequential steps:
- The BizTalk send port connects to the remote endpoint using the security protocol TLS 1.0
- If the endpoint denies the TLS 1.0 request, BizTalk will try to use the security protocol SSL3
- If the endpoint accepts this, the security protocol is agreed and the connection is established.
In some occasions, when the remote system does not support TLS 1.0, it could be that the remote system immediately terminates the connection after the first step. After that BizTalk raises the error mentioned above because it expects a proper answer from the remote system (that should state that the SSL type is not supported).
The details in the error makes sense though (especially the “Received an unexpected EOF or 0 bytes from the transport stream” part): BizTalk expects an incoming stream containing the response of the SSL request, but doesn’t receive it because the remote system immediately terminates the connection after the first TLS 1.0 request.
The solution is simple: you need to override the default SSL handshake of your WCF-Custom send port by creating your own WCF custom behavior that implements the IEndpointBehavior interface.
There is actually only one line of code needed in the ApplyClientBehavior method of your custom ServiceBehavior class (which should implement the IEndpointBehavior interface) where you set the System.Net.ServicePointManager.SecurityProtocol to System.Net.SecurityProtocolType.Ssl3:
And that does the trick: BizTalk will now initialize the SSL connection using SSL3 and it will avoid using TLS 1.0
More information about developing your own WCF behavior: http://msdn.microsoft.com/en-us/library/dd203050(BTS.10).aspx (scroll down to the Enabling Custom Behaviors and Configuring a Custom Behavior chapters, which includes a good explanation of a custom behavior that implements the IEndpointBehavior).