A functionality of one of the products I’m working on suddenly stopped working without any code changes on our side. Our application connects to a web service to get some data and that no longer worked, our customers getting the following error in their logs “The underlying connection was closed: An unexpected error occurred on a send.” The first thing to do was checking whether the web service was still up and running as expected. The requests made with SoapUI or Postman were all successful, so it was actually something in our application that was actually wrong. So I decided to use Fiddler to look at how our requests look and what do we get back. The following showed up in Fiddler:
CONNECT service.domain.com:443 HTTP/1.1 Host: service.domain.com Connection: Keep-Alive A SSLv3-compatible ClientHello handshake was found. Fiddler extracted the parameters below. Version: 3.1 (TLS/1.0) Random: 5C A7 54 A4 88 BF B9 CF 77 EF DC E8 5F 3E 24 39 8E 2B 77 7D 9B 18 3A 34 61 EE 4E EF 71 85 87 F4 "Time": 2057-05-13 23:28:12 PM SessionID: empty Extensions: server_name service.domain.com elliptic_curves unknown [0x1D), secp256r1 [0x17], secp384r1 [0x18] ec_point_formats uncompressed [0x0] SessionTicket empty extended_master_secret empty renegotiation_info 00 Ciphers: [C00A] TLS1_CK_ECDHE_ECDSA_WITH_AES_256_CBC_SHA [C009] TLS1_CK_ECDHE_ECDSA_WITH_AES_128_CBC_SHA [C014] TLS1_CK_ECDHE_RSA_WITH_AES_256_CBC_SHA [C013] TLS1_CK_ECDHE_RSA_WITH_AES_128_CBC_SHA [0035] TLS_RSA_AES_256_SHA [002F] TLS_RSA_AES_128_SHA [000A] SSL_RSA_WITH_3DES_EDE_SHA Compression: [00] NO_COMPRESSION
fiddler.network.https> HTTPS handshake to service.domain.com (for #6) failed. System.IO.IOException Unable to read data from the transport connection: An existing connection was forcibly closed by the remote host. < An existing connection was forcibly closed by the remote host
Apart from the time that was off-scale, the piece of info that drew my attention was the version, TLS 1.0. And that rang some bells about discontinuing support for this version of Transport Layer Security in some of the services we are consuming. So that looked like it happened before we had a chance to do something about it in this application that is built with C++ and .NET (the service being consumed from a .NET module).
Our target .NET framework version is 4.6.2 and by default the runtime should default to TLS 1.2. Yet, it did not work. I was able to reproduce with a demo C++ and C# application that uses a .NET class library that connects to the service. When the C# application is running it works well. When the C++ application is running, using the same class library to go to the service, it does not work. And that was the case even when I set the target to .NET framework 4.7.2 (in both the .NET class library and the C++ console project). According to all the Microsoft documents that I could find it should work well for 4.6 and above, but in my case it did not and cannot really explain it. But, there are two ways to solve this problem.
The first method involves making changes in the registry. By setting the Windows Registry key HKEY_LOCAL_MACHINE\SOFTWARE\[Wow6432Node\]Microsoft\.NETFramework\<VERSION>: SchUseStrongCrypto to 1 you enable strong cryptography that uses more secure network protocols and blocks those that are not secure. Yet, according to documentation, when I target .NET framework 4.7.2 in my application it should be implicitly set to 1:
If your app targets .NET Framework 4.6 or later versions, this key defaults to a value of 1. That’s a secure default that we recommend. If your app runs on .NET Framework 4.6, but targets an earlier version, then the key defaults to 0. In that case, you should explicitly set its value to 1.
You can set this by running the following commands in a command prompt running as administrator:
reg add HKLM\SOFTWARE\Microsoft\.NETFramework\v4.0.30319 /v SchUseStrongCrypto /t REG_DWORD /d 1 /reg:64 reg add HKLM\SOFTWARE\Microsoft\.NETFramework\v4.0.30319 /v SchUseStrongCrypto /t REG_DWORD /d 1 /reg:32
Beware, this will affect all the .NET applications running on the machine, although that shouldn’t necessarily be a bad thing.
The second method requires code changes. You can turn on TLS 1.1 and 1.2 by executing the following line of code:
System.Net.ServicePointManager.SecurityProtocol |= SecurityProtocolType.Tls11 | SecurityProtocolType.Tls12;
This should be run before you do any HTTP requests to web services in the application. This line of code does not remove support on the client side for TLS 1.0 but instructs the runtime to negociate the best of a list of available protocols that includes TLS 1.1 and 1.2. Of course, this will affect only your application and not the others running on the same machine.
To read more about this, see the following:
Thank you very much. I have a ConsoleApp with successful TLS connection and a have the simmlar code in a WinForms assembly without successful connection. The assembly uses other dlls with different versions of .net Framework. Your inline Code helps
HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\.NETFramework\v4.0.30319
Thank you so much for this. You saved my life!