Specifying SSL/TLS for System.Net.HttpWebRequest through App.config
Asked Answered
M

3

9

I need to POST JSON data to a TLS 1.2 Endpoint. I would like to have the SecurityProtocol specified in the App.config instead of being hardcoded in the source and do not want to set the registry of the machine to disable TLS 1.1.

If you do not specify the SecurityProtocol the underlying OS protocols are used but they seem to default to the least secure instead of the most secure. Because I have multiple services running from the machine I cannot set the OS to only use TLS1.2, but I still want this specific client to use TLS 1.2, and when TLS 1.3 comes out be able to modify it through application specific configuration.

This question explains how to do it via code: How to specify SSL protocol to use for WebClient class

This question explains how to do it via Registry settings for the entire machine: Are there .NET implementation of TLS 1.2?

// this is what I want to avoid
System.Net.ServicePointManager.SecurityProtocol = System.Net.SecurityProtocol.Tls12;

System.Net.HttpWebRequest request = (System.Net.HttpWebRequest)System.Net.HttpWebRequest.Create(url);

using (System.IO.StreamWriter sw = new System.IO.StreamWriter(request.GetRequestStream()))
{
    sw.write(json);
}

System.Net.HttpWebResponse response = (System.Net.HttpWebResponse)request.GetResponse();
using System.IO.StreamReader sr = new System.IO.StreamReader(response.GetResponseStream()))
{
    content = sr.ReadToEnd();
}

I do not have anything in my App.config for this client currently but that is what I would like to change.

I have found that there is a sslStreamSecurity element inside system.serviceModel, but I believe this is for ServiceReferences, not normal HttpWebRequest. I believe that is covered under system.net but I can't find an equivalent.

<system.serviceModel>
  <bindings>
    <customBinding>
      <binding name="myBinding">
        <sslStreamSecurity sslProtocls="Tls12">
      </binding>
    </customBinding>
  </bindings>
  <client>
    <endpoint address="https://myserver.com" binding="customBinding" bindingConfiguration="myBinding" name="myEndpoint" />
  </client>
</system.ServiceModel>

I am fairly open to using something other than HttpWebRequest/HttpWebResponse but would like to stay away from installing third party packages. I started down a path with System.Net.Http.HttpClient which seems newer than HttpWebRequest but quickly ran into the same issues.

Mangosteen answered 3/8, 2017 at 18:31 Comment(0)
M
2

For now I have put an integer value in the App.config that specifies the enum value to set System.Net.ServicePointManager.SecurityProtocol to. I still think there may be a more elegant way to do this and look forward to other answers. I was guided in this direction by https://mcmap.net/q/1172045/-how-to-implement-quot-securityprotocoltype-ssl-ssl1-ssl2-ssl3-quot-through-web-config-not-in-global-asax-cs

int securityProtocol = Convert.ToInt32(ConfigurationManager.AppSettings["SecurityProtocol"]);

try
{
    System.Net.ServicePointManager.SecurityProtocol = (System.Net.SecurityProtocolType)securityProtocol;
}
catch(Exception ex)
{
    Console.WriteLine("Could not setup SecurityProtocol. Try a different integer value: " + ex.Message);
    foreach (System.Net.SecurityProtocolType protocolType in Enum.GetValues(typeof(System.Net.SecurityProtocolType)))
    {
        Console.WriteLine(string.Foramt("SecurityProtocol: {0} - {1}", protocolType.ToString(), (int)protocolType));
    }
}

App.config:

<appSettings>
    <add key="SecurityProtocol" value="3072" />
</appSettings>
Mangosteen answered 9/9, 2017 at 1:4 Comment(0)
K
14

It is possible to alter the TLS settings for an application targeting <.net-4.6 without recompiling it by editing its app.config as long as you are running it on ≥.net-4.6. This is documented in “Transport Layer Security (TLS) best practices with the .NET Framework”.

When Microsoft developed .net-4.6 as an in-place replacement for .net-4.5, they wanted to make behavior changes including bugfixes, security improvements, etc.. But they did not want to break apps targeting .net-4.5 which relied on old behaviors—even buggy ones (it is quite common for old code to rely on buggy behavior and Microsoft’s .net team cares to the point of purposefully preserving bugs for the sake of compatibility). To do this, starting with .net-4.6 and continuing with later releases, each new behavior change that would be expected to cause compatibility issues was placed behind a switch which enables the old behavior and defaults to true. At compile time, the targeted framework is stored in the assembly. When the runtime loads the entry point’s assembly, it checks the targeted version and then automatically presets its compatibility switches for the targeted .net version.

If you can’t retarget or recompile your application, you can manually specify the values of these compatibility switches by adding or editing <AppContextSwitchOverrides/> in the app.config which is normally named «ExecutableName».exe.config. The switch DontEnableSystemDefaultTlsVersions was added in .net-4.7 which supports using system-provided TLS policies. The switch DontEnableSchUseStrongCrypto was added in .net-4.6 which added support for TLS 1.2.

<?xml version="1.0"?>
<configuration>
  <runtime>
    <!--
      Support connecting to servers which require modern TLS protocols.

      DontEnableSystemDefaultTlsVersions=false is sufficient if running on ≥.net-4.7
      which supports using the system-provided TLS versions/policies.
      DontEnableSchUseStrongCrypto is required if running on .net-4.6 which defaults
      to newer versions of TLS but doesn’t support following system updates/policies.
     -->
    <AppContextSwitchOverrides value="Switch.System.Net.DontEnableSystemDefaultTlsVersions=false;Switch.System.Net.DontEnableSchUseStrongCrypto=false"/>
  </runtime>
</configuration>
Kenlay answered 1/5, 2019 at 16:22 Comment(1)
For an ASP.NET app it goes in appSettings, e.g.: <add key="AppContext.SetSwitch:Switch.System.Net.DontEnableSystemDefaultTlsVersions" value="false" />Fugate
J
3

Example using the protocol name instead of integer codes.

string securityProtocol = ConfigurationManager.AppSettings["SecurityProtocol"].ToString();

try
{
    System.Net.ServicePointManager.SecurityProtocol = (System.Net.SecurityProtocolType)Enum.Parse(typeof(System.Net.SecurityProtocolType), securityProtocol);
}
catch (Exception ex)
{
    Console.WriteLine("Could not setup SecurityProtocol. Try a different integer value: " + ex.Message);
    foreach (System.Net.SecurityProtocolType protocolType in Enum.GetValues(typeof(System.Net.SecurityProtocolType)))
    {
        Console.WriteLine(string.Foramt("SecurityProtocol: {0} - {1}", protocolType.ToString(), (int)protocolType));
    }
}

For App.Config

<appSettings>
    <add key="SecurityProtocol" value="Tls12" />
</appSettings>
Joist answered 21/6, 2018 at 22:14 Comment(0)
M
2

For now I have put an integer value in the App.config that specifies the enum value to set System.Net.ServicePointManager.SecurityProtocol to. I still think there may be a more elegant way to do this and look forward to other answers. I was guided in this direction by https://mcmap.net/q/1172045/-how-to-implement-quot-securityprotocoltype-ssl-ssl1-ssl2-ssl3-quot-through-web-config-not-in-global-asax-cs

int securityProtocol = Convert.ToInt32(ConfigurationManager.AppSettings["SecurityProtocol"]);

try
{
    System.Net.ServicePointManager.SecurityProtocol = (System.Net.SecurityProtocolType)securityProtocol;
}
catch(Exception ex)
{
    Console.WriteLine("Could not setup SecurityProtocol. Try a different integer value: " + ex.Message);
    foreach (System.Net.SecurityProtocolType protocolType in Enum.GetValues(typeof(System.Net.SecurityProtocolType)))
    {
        Console.WriteLine(string.Foramt("SecurityProtocol: {0} - {1}", protocolType.ToString(), (int)protocolType));
    }
}

App.config:

<appSettings>
    <add key="SecurityProtocol" value="3072" />
</appSettings>
Mangosteen answered 9/9, 2017 at 1:4 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.