I wanted to make a post regarding an issue I ran into the other week. This issue is related to the HTTPS security protocols returned by different .NET versions. Hopefully this will spare someone a few hours (or in my case days!) of debugging.
One of our APIs call a third-party service to retrieve some data. It does this via a HTTP GET request using the .NET HttpClient. One day this HTTP called stopped working. Seemingly without any changes having been made.
The error message we got was “Authentication failed because the remote party has closed the transport stream.”.
A quick Google returns a couple of stackoverflow threads.
They both seem to suggest that the problem is that the server does not support the encryption protocol that we are trying to use.
The guys in our OPS department helpfully pointed me in the direction of SSL Labs. This site allows you to analyse the security settings of a website. Among other things it will tell you which protocols that are supported.
As you can see in the screenshot below the site in question only supports TLS 1.1 and TLS 1.2.
We confirmed with our third-party provider that they had removed support for TLS 1.0. This is why the call started failing.
The solution – Option 1
The stackoverflow threads above suggest that the solution is to set the allowed protocols. For example, to only allow TLS 1.1 and TLS 1.2 you would execute the code below.
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12 | SecurityProtocolType.Tls11;
This forces .NET to only use these protocols. Setting this in our API resolved the issue. However, this has potential downsides. Setting this explicitly in code locks us to these protocols. What if there is a problem with TLS1.1? We would have to update all of our applications to remove it. It would be better if we removed the options we do not want. For example, running the code below would remove the usage of SSL3.
System.Net.ServicePointManager.SecurityProtocol &= ~SecurityProtocolType.Ssl3;
However, the best option would be if we didn’t have to add any code at all.
The solution – Option 2
According to stackoverflow, in .NET 4.6 and above the HTTP client should default to the most secure protocol. Our API is targeting .NET 4.6.1. So why is it not defaulting to TLS 1.2?
I had a look at the configuration of the API and found this entry
<system.web> <compilation debug="true" targetFramework="4.6.1" /> <httpRuntime targetFramework="4.5" /> </system.web>
As you can see the target framework is 4.6.1 but the httpRuntime is set to 4.5.
According to this stackoverflow thread the default security protocols in .NET 4.5 are SSL3 and TSL1.0. I tried changing the httpRuntime to 4.6.1 and this resolves the issue. I assume that having the httpRuntime set to 4.5 means it will use the default security protocols of .NET 4.5.
That leaves us with one final unanswered question. Why is the httpRuntime element set to 4.5?
It turns out that when you upgrade the target framework of a .NET project the httpRuntime is not automatically updated. This stackoverflow thread indicates that it is probably for backward compatibility reasons. If you look at the Microsoft documentation I could not find any reason for us to not set the httpRuntime to 4.6.1. I imagine that the only reason it was not done in the first place was just an oversight.
We have now released our service with the updated framework and everything is working well so far.
Before you go
The implementation of the default security protocol changes again in .NET 4.7. It now defaults to the default implementation set in the OS. This could of course lead to some interesting issues and is definitely something to check before upgrading. More details on this here.
As of June 2018, sites that need to be PCI compliant will have to disable SSL and TLS 1.0. This means that more and more sites will only support TLS 1.1 and TLS1.2 and making sure you use these protocols become even more important. Additional information can be found here.