[.net] Default SecurityProtocol in .NET 4.5

What is the default security protocol for communicating with servers that support up to TLS 1.2? Will .NET by default, choose the highest security protocol supported on the server side or do I have to explicitly add this line of code:

System.Net.ServicePointManager.SecurityProtocol = 
SecurityProtocolType.Tls | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls12;

Is there a way to change this default, besides a code change?

Lastly, does .NET 4.0 only support up to TLS 1.0? i.e. I have to upgrade client projects to 4.5 to support TLS 1.2.

My motivation is to remove support for SSLv3 on the client side even if server supports it (I already have a powershell script to disable this in the machine registry) and to support the highest TLS protocol that the server supports.

Update: Looking at the ServicePointManager class in .NET 4.0 I see no enumerated values for TLS 1.0 and 1.1. In both .NET 4.0/4.5, the default is SecurityProtocolType.Tls|SecurityProtocolType.Ssl3. Hopefully this default won't break by disabling SSLv3 in the registry.

However, I've decided I have to upgrade all apps to .NET 4.5 and to explicitly add SecurityProtocolType.Tls | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls12; anyway to all bootstrapping code of all applications.

This will make outbound requests to various apis and services to not downgrade to SSLv3 and should select the highest level of TLS.

Does this approach sound reasonable or overkill? I have many applications to update, and I want to future proof them since I hear even TLS 1.0 may be deprecated in the near future by some providers.

As a client making outbound requests to APIs, does disabling SSL3 in the registry even have an effect in the .NET framework? I see by default, TLS 1.1 and 1.2 are not enabled, do we have to enable it via the registry? RE http://support.microsoft.com/kb/245030.

After a bit of investigation, I believe the registry settings will have no affect since they apply to IIS (server subkey) and browsers (client subkey).

Sorry this post turned into multiple questions, followed up with "maybe" answers.

This question is related to .net security ssl

The answer is


According to Transport Layer Security (TLS) best practices with the .NET Framework: To ensure .NET Framework applications remain secure, the TLS version should not be hardcoded. Instead set the registry keys: SystemDefaultTlsVersions and SchUseStrongCrypto:

Windows Registry Editor Version 5.00

[HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Microsoft\.NETFramework\v2.0.50727]
"SystemDefaultTlsVersions"=dword:00000001
"SchUseStrongCrypto"=dword:00000001

[HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Microsoft\.NETFramework\v4.0.30319]
"SystemDefaultTlsVersions"=dword:00000001
"SchUseStrongCrypto"=dword:00000001

[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\.NETFramework\v2.0.50727]
"SystemDefaultTlsVersions"=dword:00000001
"SchUseStrongCrypto"=dword:00000001

[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\.NETFramework\v4.0.30319]
"SystemDefaultTlsVersions"=dword:00000001
"SchUseStrongCrypto"=dword:00000001

If you can use .NET 4.7.1 or newer, it will use TLS 1.2 as the minimum protocol based on the operating system capabilities. Per Microsoft recommendation :

To ensure .NET Framework applications remain secure, the TLS version should not be hardcoded. .NET Framework applications should use the TLS version the operating system (OS) supports.

I got the problem when my customer upgraded TLS from 1.0 to 1.2. My application is using .net framework 3.5 and run on server. So i fixed it by this way:

  1. Fix the program

Before call HttpWebRequest.GetResponse() add this command:

ServicePointManager.SecurityProtocol = SecurityProtocolType.Ssl3 | SecurityProtocolType.Tls | SecurityProtocolTypeExtensions.Tls11 | SecurityProtocolTypeExtensions.Tls12;

Extensions 2 DLLs by adding 2 new classes: System.Net and System.Security.Authentication

    namespace System.Net
    {
        using System.Security.Authentication;
        public static class SecurityProtocolTypeExtensions
        {
            public const SecurityProtocolType Tls12 = (SecurityProtocolType)SslProtocolsExtensions.Tls12;
            public const SecurityProtocolType Tls11 = (SecurityProtocolType)SslProtocolsExtensions.Tls11;
            public const SecurityProtocolType SystemDefault = (SecurityProtocolType)0;
        }
    } 

    namespace System.Security.Authentication
    {
        public static class SslProtocolsExtensions
        {
            public const SslProtocols Tls12 = (SslProtocols)0x00000C00;
            public const SslProtocols Tls11 = (SslProtocols)0x00000300;
        }
    } 
  1. Update Microsoft batch

Download batch:

  • For windows 2008 R2: windows6.1-kb3154518-x64.msu
  • For windows 2012 R2: windows8.1-kb3154520-x64.msu

For download batch and more details you can see here:

https://support.microsoft.com/en-us/help/3154518/support-for-tls-system-default-versions-included-in-the-.net-framework-3.5.1-on-windows-7-sp1-and-server-2008-r2-sp1


The BEST solution to this problem appears to be to upgrade to at least .NET 4.6 or later, which will automatically choose strong protocols as well as strong ciphers.

If you can't upgrade to .NET 4.6, the advice of setting

System.Net.ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls11 | SecurityProtocolType.Tls12;

And using the registry settings:

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft.NETFramework\v4.0.30319 – SchUseStrongCrypto = DWORD of 1 HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft.NETFramework\v4.0.30319 – SchUseStrongCrypto = DWORD of 1

Results in using something other than TLS 1.0 and a strong cipher.

In my testing, only the setting in the Wow6432Node made any difference, even though my test application was built for Any CPU.


Create a text file with a .reg extension and the following contents:

Windows Registry Editor Version 5.00

[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\.NETFramework\v4.0.30319]
"SchUseStrongCrypto"=dword:00000001

[HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\.NETFramework\v4.0.30319]
"SchUseStrongCrypto"=dword:00000001

Or download it from the following source:

https://tls1test.salesforce.com/s/NET40-Enable-TLS-1_2.reg

Double-click to install...


Microsoft recently published best practices around this. https://docs.microsoft.com/en-us/dotnet/framework/network-programming/tls

Summary

Target .Net Framework 4.7, remove any code setting the SecurityProtocol, thus the OS will ensure you use the most secure solution.

NB: You will also need to ensure that the latest version of TLS is supported & enabled on your OS.

OS                          TLS 1.2 support

Windows 10                  \_ Supported, and enabled by default.
Windows Server 2016         /   
Windows 8.1                 \_ Supported, and enabled by default.
Windows Server 2012 R2      /
Windows 8.0                 \_ Supported, and enabled by default.
Windows Server 2012         /
Windows 7 SP1               \_ Supported, but not enabled by default*.
Windows Server 2008 R2 SP1  /
Windows Server 2008         -  Support for TLS 1.2 and TLS 1.1 requires an update. See Update to add support for TLS 1.1 and TLS 1.2 in Windows Server 2008 SP2.
Windows Vista               -  Not supported.

* To enable TLS1.2 via the registry see https://docs.microsoft.com/en-us/windows-server/security/tls/tls-registry-settings#tls-12 

    Path: HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS1.2\Server

        Property: Enabled
        Type: REG_DWORD
        Value: 1

        Property: DisabledByDefault 
        Type: REG_DWORD
        Value: 0

    Path: HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS1.2\Client

        Property: Enabled
        Type: REG_DWORD
        Value: 1

        Property: DisabledByDefault 
        Type: REG_DWORD
        Value: 0

For more information and older frameworks, please refer to the MS link.


An alternative to hard-coding ServicePointManager.SecurityProtocol or the explicit SchUseStrongCrypto key as mentioned above:
You can tell .NET to use the default SCHANNEL settings with the SystemDefaultTlsVersions key,
e.g.:

[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\.NETFramework\v4.0.30319] "SystemDefaultTlsVersions"=dword:00000001
[HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\.NETFramework\v4.0.30319] "SystemDefaultTlsVersions"=dword:00000001

I'm running under .NET 4.5.2, and I wasn't happy with any of these answers. As I'm talking to a system which supports TLS 1.2, and seeing as SSL3, TLS 1.0, and TLS 1.1 are all broken and unsafe for use, I don't want to enable these protocols. Under .NET 4.5.2, the SSL3 and TLS 1.0 protocols are both enabled by default, which I can see in code by inspecting ServicePointManager.SecurityProtocol. Under .NET 4.7, there's the new SystemDefault protocol mode which explicitly hands over selection of the protocol to the OS, where I believe relying on registry or other system configuration settings would be appropriate. That doesn't seem to be supported under .NET 4.5.2 however. In the interests of writing forwards-compatible code, that will keep making the right decisions even when TLS 1.2 is inevitably broken in the future, or when I upgrade to .NET 4.7+ and hand over more responsibility for selecting an appropriate protocol to the OS, I adopted the following code:

SecurityProtocolType securityProtocols = ServicePointManager.SecurityProtocol;
if (securityProtocols.HasFlag(SecurityProtocolType.Ssl3) || securityProtocols.HasFlag(SecurityProtocolType.Tls) || securityProtocols.HasFlag(SecurityProtocolType.Tls11))
{
    securityProtocols &= ~(SecurityProtocolType.Ssl3 | SecurityProtocolType.Tls | SecurityProtocolType.Tls11);
    if (securityProtocols == 0)
    {
        securityProtocols |= SecurityProtocolType.Tls12;
    }
    ServicePointManager.SecurityProtocol = securityProtocols;
}

This code will detect when a known insecure protocol is enabled, and in this case, we'll remove these insecure protocols. If no other explicit protocols remain, we'll then force enable TLS 1.2, as the only known secure protocol supported by .NET at this point in time. This code is forwards compatible, as it will take into consideration new protocol types it doesn't know about being added in the future, and it will also play nice with the new SystemDefault state in .NET 4.7, meaning I won't have to re-visit this code in the future. I'd strongly recommend adopting an approach like this, rather than hard-coding any particular security protocol states unconditionally, otherwise you'll have to recompile and replace your client with a new version in order to upgrade to a new security protocol when TLS 1.2 is inevitably broken, or more likely you'll have to leave the existing insecure protocols turned on for years on your server, making your organisation a target for attacks.


Following code will:

  • print enabled protocols
  • print available protocols
  • enable TLS1.2 if platform supports it and if it is not enabled to begin with
  • disable SSL3 if it is enabled
  • print end result

Constants:

  • 48 is SSL3
  • 192 is TLS1
  • 768 is TLS1.1
  • 3072 is TLS1.2

Other protocols will not be affected. This makes this compatible with future protocols (Tls1.3, etc).

Code

// print initial status
    Console.WriteLine("Runtime: " + System.Diagnostics.FileVersionInfo.GetVersionInfo(typeof(int).Assembly.Location).ProductVersion);
    Console.WriteLine("Enabled protocols:   " + ServicePointManager.SecurityProtocol);
    Console.WriteLine("Available protocols: ");
    Boolean platformSupportsTls12 = false;
    foreach (SecurityProtocolType protocol in Enum.GetValues(typeof(SecurityProtocolType))) {                
        Console.WriteLine(protocol.GetHashCode());
        if (protocol.GetHashCode() == 3072){
            platformSupportsTls12 = true;
        }
    }
    Console.WriteLine("Is Tls12 enabled: " + ServicePointManager.SecurityProtocol.HasFlag((SecurityProtocolType)3072));    


// enable Tls12, if possible
    if (!ServicePointManager.SecurityProtocol.HasFlag((SecurityProtocolType)3072)){
        if (platformSupportsTls12){
            Console.WriteLine("Platform supports Tls12, but it is not enabled. Enabling it now.");
            ServicePointManager.SecurityProtocol |= (SecurityProtocolType)3072;
        } else {
            Console.WriteLine("Platform does not supports Tls12.");
        }
    }

// disable ssl3
   if (ServicePointManager.SecurityProtocol.HasFlag(SecurityProtocolType.Ssl3)) { 
      Console.WriteLine("Ssl3SSL3 is enabled. Disabling it now.");
      // disable SSL3. Has no negative impact if SSL3 is already disabled. The enclosing "if" if just for illustration.
      System.Net.ServicePointManager.SecurityProtocol &= ~SecurityProtocolType.Ssl3;                      
   }
    Console.WriteLine("Enabled protocols:   " + ServicePointManager.SecurityProtocol);

Output

Runtime: 4.7.2114.0
Enabled protocols:   Ssl3, Tls
Available protocols: 
0
48
192
768
3072
Is Tls12 enabled: False
Platform supports Tls12, but it is not enabled. Enabling it now.
Ssl3 is enabled. Disabling it now.
Enabled protocols:   Tls, Tls12

You can override the default behavior in following registry:

Key  : HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\.NETFramework\v4.0.30319 
Value: SchUseStrongCrypto
Type: REG_DWORD
Data : 1

and

Key  : HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Microsoft\.NETFramework\v4.0.30319
Value: SchUseStrongCrypto
Type: REG_DWORD
Data : 1

For details, please see the implementation of ServicePointManager.


I have found that when I specify only TLS 1.2 that it will still down negotiate to 1.1. System.Net.ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;

I have specified this in the Global.asax startup method for my .net 4.5 web app.


There are two possible scenario,

  1. If you application run on .net framework 4.5 or less than that and you can easily deploy new code to the production then you can use of below solution.

    You can add below line of code before making api call,

    ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12; // .NET 4.5

  2. If you cannot deploy new code and you want to resolve with the same code which is present in the production, then you have two options.

Option 1 :

[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\.NETFramework\v4.0.30319]
"SchUseStrongCrypto"=dword:00000001

[HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\.NETFramework\v4.0.30319]
"SchUseStrongCrypto"=dword:00000001


then create a file with extension .reg and install.

Note : This setting will apply at registry level and is applicable to all application present on that machine and if you want to restrict to only single application then you can use Option 2

Option 2 : This can be done by changing some configuration setting in config file. You can add either of one in your config file.

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

or

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

The default System.Net.ServicePointManager.SecurityProtocol in both .NET 4.0/4.5 is SecurityProtocolType.Tls|SecurityProtocolType.Ssl3.

.NET 4.0 supports up to TLS 1.0 while .NET 4.5 supports up to TLS 1.2

However, an application targeting .NET 4.0 can still support up to TLS 1.2 if .NET 4.5 is installed in the same environment. .NET 4.5 installs on top of .NET 4.0, replacing System.dll.

I've verified this by observing the correct security protocol set in traffic with fiddler4 and by manually setting the enumerated values in a .NET 4.0 project:

ServicePointManager.SecurityProtocol = (SecurityProtocolType)192 |
(SecurityProtocolType)768 | (SecurityProtocolType)3072;

Reference:

namespace System.Net
{
    [System.Flags]
    public enum SecurityProtocolType
    {
       Ssl3 = 48,
       Tls = 192,
       Tls11 = 768,
       Tls12 = 3072,
    }
}

If you attempt the hack on an environment with ONLY .NET 4.0 installed, you will get the exception:

Unhandled Exception: System.NotSupportedException: The requested security protocol is not supported. at System.Net.ServicePointManager.set_SecurityProtocol(SecurityProtocolType v alue)

However, I wouldn't recommend this "hack" since a future patch, etc. may break it.*

Therefore, I've decided the best route to remove support for SSLv3 is to:

  1. Upgrade all applications to .NET 4.5
  2. Add the following to boostrapping code to override the default and future proof it:

    System.Net.ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls12;

*Someone correct me if this hack is wrong, but initial tests I see it works


For Key: HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft.NETFramework\v4.0.30319 Value: SchUseStrongCrypto

You have to set the value to 1.


For completeness, here is a Powershell script that sets aforementioned registry keys:

new-itemproperty -path "HKLM:\SOFTWARE\Microsoft\.NETFramework\v4.0.30319" -name "SchUseStrongCrypto" -Value 1 -PropertyType "DWord";
new-itemproperty -path "HKLM:\SOFTWARE\Wow6432Node\Microsoft\.NETFramework\v4.0.30319" -name "SchUseStrongCrypto" -Value 1 -PropertyType "DWord"

The registry change mechanism worked for me after a struggle. Actually my application was running as 32bit. So I had to change the value under path.

HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft.NETFramework\v4.0.30319

The value type needs to be DWORD and value above 0 .Better use 1.Registry settings to get .Net 4.0 app use TLS 1.2 provided .Net 4.5 is installed in the machine.


Examples related to .net

You must add a reference to assembly 'netstandard, Version=2.0.0.0 How to use Bootstrap 4 in ASP.NET Core No authenticationScheme was specified, and there was no DefaultChallengeScheme found with default authentification and custom authorization .net Core 2.0 - Package was restored using .NetFramework 4.6.1 instead of target framework .netCore 2.0. The package may not be fully compatible Update .NET web service to use TLS 1.2 EF Core add-migration Build Failed What is the difference between .NET Core and .NET Standard Class Library project types? Visual Studio 2017 - Could not load file or assembly 'System.Runtime, Version=4.1.0.0' or one of its dependencies Nuget connection attempt failed "Unable to load the service index for source" Token based authentication in Web API without any user interface

Examples related to security

Monitoring the Full Disclosure mailinglist Two Page Login with Spring Security 3.2.x How to prevent a browser from storing passwords JWT authentication for ASP.NET Web API How to use a client certificate to authenticate and authorize in a Web API Disable-web-security in Chrome 48+ When you use 'badidea' or 'thisisunsafe' to bypass a Chrome certificate/HSTS error, does it only apply for the current site? How does Content Security Policy (CSP) work? How to prevent Screen Capture in Android Default SecurityProtocol in .NET 4.5

Examples related to ssl

Requests (Caused by SSLError("Can't connect to HTTPS URL because the SSL module is not available.") Error in PyCharm requesting website A fatal error occurred while creating a TLS client credential. The internal error state is 10013 curl: (35) error:1408F10B:SSL routines:ssl3_get_record:wrong version number How to install OpenSSL in windows 10? ssl.SSLError: tlsv1 alert protocol version Invalid self signed SSL cert - "Subject Alternative Name Missing" "SSL certificate verify failed" using pip to install packages ssl.SSLError: [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed (_ssl.c:749) Powershell Invoke-WebRequest Fails with SSL/TLS Secure Channel "ssl module in Python is not available" when installing package with pip3