TLS 1.2 not negotiated in .NET 4.7 without explicit ServicePointManager.SecurityProtocol call
Asked Answered
O

7

57

I need to upgrade a .NET application to support a call to an API on a website that only supports TLS 1.2. From what I read, if the application is targeting 4.6 or higher then it will use TLS 1.2 by default.

To test I created a Windows Forms app that targets 4.7. Unfortunately it errors when I don't explicitly set ServicePointManager.SecurityProtocol. Here is the code:

HttpClient _client = new HttpClient();

var msg = new StringBuilder();

// If I uncomment the next line it works, but fails even with 4.7
// ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;

var httpWebRequest = (HttpWebRequest)WebRequest.Create("https://sandbox.authorize.net");

httpWebRequest.KeepAlive = false;

try
{
    var httpWebResponse = (HttpWebResponse) httpWebRequest.GetResponse();

    msg.AppendLine("The HTTP request Headers for the first request are: ");

    foreach (var header in httpWebRequest.Headers)
    {
        msg.AppendLine(header.ToString());
    }

    ResponseTextBox.Text = msg.ToString();

}
catch (Exception exception)
{
   ResponseTextBox.Text = exception.Message;

   if (exception.InnerException != null)
   {
       ResponseTextBox.Text += Environment.NewLine + @"  ->" + exception.InnerException.Message;

       if (exception.InnerException.InnerException != null)
       {
            ResponseTextBox.Text += Environment.NewLine + @"     ->" + exception.InnerException.InnerException.Message;
       }
   }
}

If you uncomment out the following line:

ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;

it works. This isn't a good solution since it hard codes what TLS version to use, so it wouldn't use TLS 1.3 in future.

What else do I need to do to get it work without having this line. I'm testing from a Window 10 machine with 4.7 installed.

Update

I tried a test with HttpClient and had the same results, I had to explicitly set SecurityProtocol.

Code:

var msg = new StringBuilder();

// Need to uncomment code below for TLS 1.2 to be used
// ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;

try
{
   var response = await _client.GetAsync(@"https://sandbox.authorize.net");

   msg.AppendLine("response.IsSuccessStatusCode : " + response.IsSuccessStatusCode);

   msg.AppendLine(await response.Content.ReadAsStringAsync());

   textBox.Text = msg.ToString();
  }

  catch (Exception exception)
  {
      textBox.Text = exception.Message;

      if (exception.InnerException != null)
      {
          textBox.Text += Environment.NewLine + @"  ->" + exception.InnerException.Message;
      }
   }
Oliy answered 25/6, 2017 at 22:27 Comment(10)
You can OR the tls versions together to support them all if you want. But to avoid the need for this use HttpClient (if you can make your code async without any blocking calls)Heathenry
I don't want to define the security protocol because when a new one, such as TLS 1.3 becomes available, it wouldn't be used because it isn't in the list. I'll rewrite my test with HttpClient, is there a difference the default Security Protocol used by HttpClient vs HttpWebResponse?Oliy
Don't take my word for it, but I assume HttpClient will support negotiation of the highest protocol available for the version of CLR. So, your next brick wall will require a CLR upgrade instead of code upgrade. This is probably a horrible idea, but I wonder what would happen if you just turn on all bits: SecurityProtocolType s = (SecurityProtocolType)2147483647;Heathenry
@Crowcoder, same result with HttpClient, updated question code used.Oliy
Interesting. Check out this release note about v4.7 github.com/Microsoft/dotnet/blob/master/releases/net47/…Heathenry
It is interesting. I tried to set it to "SystemDefault" but that failed. Assuming it is the default value, then TLS 1.2 should be the value used on Win10 OS.Oliy
If this was a web application, would setting SecurityProtocolType affect both the "outgoing" traffic from the web app (such as someone calling a http GET method to your site) and external calls made from within the app to some external service? (using HttpClient for example)Pennate
"If I uncomment the next line it works, but fails even with 4.7" It works but it fails? What do you mean? Does it work if you set it to SecurityProtocolType.Tls12 | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls or does it work only when you set it to SecurityProtocolType.Tls12?Olivine
The .NET 4.7 documentation is incorrect. It will not use the best available protocol but the “OS default” one. On Windows 7 this is TLS 1.0 and .NET 4.7 will not negotiate a better protocol version. That’s why connecting to servers which run newer TLS will fail.Vallejo
It is only 4.7.1 or above that uses OS TLS version to negotiate. WCF Supports TLS1.0, 1.1 and 1.2 as the default in .NET Framework 4.7. Starting with .NET Framework 4.7.1, WCF defaults to the operating system configured version. If an application is explicitly configured with SslProtocols.None, WCF uses the operating system default setting when using the NetTcp transport.Bibby
O
9

I've found one solution. It doesn't answer the question about why TLS 1.2 isn't being used by default on Win10 with .NET 4.7, but it does allow me not to have to set ServicePointManager.SecurityProtocol.

The solution that worked from both my 4.5.2 and 4.7 test apps is to add the following to app.config:

<AppContextSwitchOverrides value="Switch.System.Net.DontEnableSchUseStrongCrypto=false"/>

Here the whole app.config:

<?xml version="1.0" encoding="utf-8"?>
<configuration>
    <startup> 
        <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.7"/>
    </startup>
    <runtime>
      <AppContextSwitchOverrides value="Switch.System.Net.DontEnableSchUseStrongCrypto=false"/>
    </runtime>
</configuration>
Oliy answered 26/6, 2017 at 17:54 Comment(4)
This make no sense. The default value of Switch.System.Net.DontEnableSchUseStrongCrypto is already false. false: don't use insecure protocols (SSL), true: allow insecure protocols. This should not be related to TLS1.2 at all. I don't deny it is working (I have not tested), just saying it make no sense.Olivine
I tried this in a simple console application that makes a HttpClient request to a TLS 1.2-only web-service and I ran the console program on Windows 7 SP1. Unfortunately it had zero effect. Downvoting.Ephialtes
I've tried all of the answers on this thread on a windows 10 machine running a unit test project in 4.7.2 and the only thing that works is hard coding ServicePointManager.SecurityProtocol |= SecurityProtocolType.Tls12; -- shakes fist at MicrosoftSchopenhauer
Yes, this solution has no effect on Windows 7 SP1.Vallejo
O
51

I had the same issue (Windows 10 and SSL3 / TLS only... not System Default) with a legacy app targeting 4.7.2. My issue was that during the upgrade process over the years we never added in the targetFramework to the system.web > httpRuntime element (Note: it did exist on system.web > compilation element). Before taking bigger steps, ensure your system.web looks something like the following:

<system.web>
    <compilation targetFramework="4.7.2"></compilation>
    <httpRuntime targetFramework="4.7.2" />
</system.web>

In the above example, swap 4.7.2 for whatever version of the framework you are currently using that is >= 4.7.

Ohl answered 6/4, 2019 at 21:11 Comment(6)
This is exactly what I had. The same, we upgraded an app to 4.7.2 and forgot to change the httpRuntime.Compilation
Same here, got the old tlsAquarius
Thanks, this fixed the issue...Can anyone let me know why httpRuntime isn't automatically set to 4.7.2 after the project is upgraded to .Net 4.7?Crashland
Tx you did my trick !Tebet
Awesome! Solved my problem. I had upgraded to 4.7.2 but didn't change the web config: <httpRuntime targetFramework="4.7.2" maxRequestLength="15360" />Epitaph
This was a very strange issue for me. On my development computer, I had one website which could not connect to DocuSign (using the latest SDK), but another that could. They were both going through the same in-house assembly to make the DocuSign call, so I figured it had to be a configuration issue. On the one that was failing (legacy code), the targetFramework = "4.7.2" setting was missing on httpRuntime. Adding that -- it all works now. Thanks much!!!Alcantar
H
23

Starting with apps that target the .NET Framework 4.7, the default value of the ServicePointManager.SecurityProtocol property is SecurityProtocolType.SystemDefault.

This change allows .NET Framework networking APIs based on SslStream (such as FTP, HTTPS, and SMTP) to inherit the default security protocols from the operating system instead of using hard-coded values defined by the .NET Framework.

That's the reason of the new behaviour you experienced and the need of the new configuration:

<runtime>
   <AppContextSwitchOverrides value="Switch.System.ServiceModel.DisableUsingServicePointManagerSecurityProtocols=false;Switch.System.Net.DontEnableSchUseStrongCrypto=false" /> 
</runtime>

See here and here

Update (useful info)

Keep in mind, best security practices suggest to update your IIS configuration disabling, time by time, old protocols and ciphers key (e.g. TLS 1.0, 1.1). See Setup Microsoft Windows or IIS for SSL Perfect Forward Secrecy and TLS 1.2 for very interesting info.

If you follow this practice, you don't need to set the configuration above (as MS suggests), because your Win server / IIS is already well configured.

Of course, this is possible only if you have access to the server with proper grants.

Houdon answered 7/10, 2017 at 6:44 Comment(0)
T
11

As an alternative to Nick Y's answer, I discovered that on Windows 7 using .NET 4.7+, I needed to enable these registry settings in order for the Microsoft Secure Channel (Schannel) package to properly send TLS1.1 and TLS1.2.

This allows the .NET client to continue to have System.Net.ServicePointManager.SecurityProtocol set to SystemDefault and get TLS 1.1 and 1.2 on a Windows 7 computer.

Using the SystemDefault option allows .NET to defer the selection of protocols to the OS. This means that when Microsoft releases hotfixes to the OS to disable insecure protocols or enables support for new ones in their native SCHANNEL library, .NET framework apps running will automatically get this new behavior.

Here are the registry entries:

[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.1\Client]
"DisabledByDefault"=dword:00000000

[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.2\Client]
"DisabledByDefault"=dword:00000000
Trigger answered 13/7, 2018 at 18:53 Comment(9)
Worked for 2008 R2 as well.Reichert
"This allows the .NET client to continue to have System.Net.ServicePointManager.SecurityProtocol set to SystemDefault" Won't "Switch.System.Net.DontEnableSystemDefaultTlsVersions=true" also allow SecurityProtocol to be SystemDefault? In case it is better to edit config file than messing with registry IMO.Olivine
@Olivine no. If you set "DontEnableSystemDefaultTlsVersion=true" you disable the OS's default protocols and only enable the default protocols in the installed version of .NET. In my case, If the OS is patched to enable new protocols, I want them enabled. I didn't want to update the application's config or .NET framework version just to enable new versions of TLS.Trigger
@Olivine See my updated answer as to why this might be advantageous vs changing the app to disable the SystemDefault.Trigger
".NET framework apps running will automatically get this new behavior" yes it sounds good, but it does not seem to work in real life when they must be enabled in registry as well? Imagine tls1.3 came, this too would not work without a new registry change? Not very automatic.Olivine
@Olivine I'm pointing out that this might be a OS configuration issue, not a .NET application problem.Trigger
Agreed with Merick, using SystemDefault is recommended by Microsoft and enabling newer version TLS should be the job of windows update/hotfix;Brightness
Another found to mention: if the targetFramework attribute value > 4.7 in <httpruntime>, the following AppContextSwitch command could not disable system default security protocol for web applications (however it works for console application) <runtime> <AppContextSwitchOverrides value="Switch.System.Net.DontEnableSystemDefaultTlsVersions=true"/> </runtime>Brightness
@Olivine Managing the system default (which happens to be in the registry) for an enterprise via group policies is going to scale much better than trying to find and maintain individual application settings.Lamont
O
9

I've found one solution. It doesn't answer the question about why TLS 1.2 isn't being used by default on Win10 with .NET 4.7, but it does allow me not to have to set ServicePointManager.SecurityProtocol.

The solution that worked from both my 4.5.2 and 4.7 test apps is to add the following to app.config:

<AppContextSwitchOverrides value="Switch.System.Net.DontEnableSchUseStrongCrypto=false"/>

Here the whole app.config:

<?xml version="1.0" encoding="utf-8"?>
<configuration>
    <startup> 
        <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.7"/>
    </startup>
    <runtime>
      <AppContextSwitchOverrides value="Switch.System.Net.DontEnableSchUseStrongCrypto=false"/>
    </runtime>
</configuration>
Oliy answered 26/6, 2017 at 17:54 Comment(4)
This make no sense. The default value of Switch.System.Net.DontEnableSchUseStrongCrypto is already false. false: don't use insecure protocols (SSL), true: allow insecure protocols. This should not be related to TLS1.2 at all. I don't deny it is working (I have not tested), just saying it make no sense.Olivine
I tried this in a simple console application that makes a HttpClient request to a TLS 1.2-only web-service and I ran the console program on Windows 7 SP1. Unfortunately it had zero effect. Downvoting.Ephialtes
I've tried all of the answers on this thread on a windows 10 machine running a unit test project in 4.7.2 and the only thing that works is hard coding ServicePointManager.SecurityProtocol |= SecurityProtocolType.Tls12; -- shakes fist at MicrosoftSchopenhauer
Yes, this solution has no effect on Windows 7 SP1.Vallejo
C
7

I am on Windows 7 and .NET 4.7.1

The recommendation to use Switch.System.ServiceModel.DisableUsingServicePointManagerSecurityProtocols and Switch.System.Net.DontEnableSchUseStrongCrypto mentioned in two other answers did not work in my project and OP's code was failing too.

Reviewing source code for ServicePointManager and LocalAppContextSwitches I came across another config setting which worked.

<runtime>
  <AppContextSwitchOverrides value="Switch.System.Net.DontEnableSystemDefaultTlsVersions=true" />
</runtime>
Causative answered 22/6, 2018 at 20:9 Comment(2)
See my answer for an alternative which still allows .NET to keep its "SystemDefaultTls" settingTrigger
This config setting works for console application only. Web application targeting to 4.7 or above will always have System.Net.ServicePointManager.SecurityProtocol = SystemDefault unless it is hard-coded explicitly to other protocols (which is not recommended)Brightness
P
1

I had the same problem for a Word VSTO add-in project which initially developed using .NET framework 4.5.2 and then upgraded into 4.7. Tried all the solutions in here, but none of them worked for me. Finally found this article.

For those who are having the same problem as me for VSTO add-in projects, add these code in the ThisAddIn.cs class, it worked for me.

AppContext.SetSwitch("Switch.System.ServiceModel.DisableUsingServicePointManagerSecurityProtocols", false);
AppContext.SetSwitch("Switch.System.Net.DontEnableSchUseStrongCrypto", false);
Pithecanthropus answered 2/3, 2022 at 9:50 Comment(0)
M
0

Windows 7 has TLS1.2 and TLS1.1 disabled by default. Enabling it in IE has no effect for other applications.

You should enable it by registry: [HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.2\Client] "DisabledByDefault"=dword:00000000

Very useful article with script to set as only TLS1.2

Microminiaturization answered 19/3, 2021 at 11:24 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.