Configure ServicePointManager.SecurityProtocol through AppSettings
Tags: dotnet
Published: 2020-10-25 20:45:11

---
title: Configure ServicePointManager.SecurityProtocol through AppSettings
published: true
date: 2020-10-21 00:00:00 UTC
tags: dotnet
canonical_url: https://swimburger.net/blog/dotnet/configure-servicepointmanager-securityprotocol-through-appsettings
---

As time passes, webservers move to newer security protocols and remove deprecated security protocols. This may lead to errors in your application when you make HTTP requests. Usually .NET automatically finds a security protocol in common, but sometimes you have to update `ServicePointManager.SecurityProtocol` explicitly.

You can remove security protocols using a binary AND operator and a Bitwise complement to inverse the bits:

```csharp
// removes SSL3
ServicePointManager.SecurityProtocol &= ~SecurityProtocolType.Ssl3;
// remove multiple protocols, SSL3 and TLS 1.0
ServicePointManager.SecurityProtocol &= ~(SecurityProtocolType.Ssl3 | SecurityProtocolType.Tls);

```

You can add security protocols using a Binary OR operator:

```csharp
// add TLS 1.1
ServicePointManager.SecurityProtocol |= SecurityProtocolType.Tls11;
// add multiple protocols, TLS 1.1 and TLS 1.2
ServicePointManager.SecurityProtocol |= SecurityProtocolType.Tls11 | SecurityProtocolType.Tls12;

```

Instead of hardcoding the additions/removals into your codebase, you could also use config-files with appSettings. Here's an example of what your configuration file would look like:

```xml
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <startup>
    <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.7.2" />
  </startup>
  <appSettings>
    <add key="SecurityProtocols.Remove" value="Ssl3,Tls" />
    <add key="SecurityProtocols.Add" value="Tls11,Tls12" />
  </appSettings>
</configuration>

```

In your application, run the following methods `RemoveSecurityProtocols` then `AddSecurityProtocols`:

```csharp
private static void RemoveSecurityProtocols()
{
    var securityProtocolsToRemove = ConfigurationManager.AppSettings["SecurityProtocols.Remove"]
        ?.Split(',')
        .Select(o => o.Trim())
        .ToArray();

    if (securityProtocolsToRemove == null || securityProtocolsToRemove.Length == 0)
    {
        return;
    }

    foreach (var securityProtocolString in securityProtocolsToRemove)
    {
        SecurityProtocolType securityProtocolEnum;
        if (Enum.TryParse(securityProtocolString, out securityProtocolEnum))
        {
            // removes security protocol using binary operation
            ServicePointManager.SecurityProtocol &= ~securityProtocolEnum;
        }
    }
}

private static void AddSecurityProtocols()
{
    var securityProtocolsAdd = ConfigurationManager.AppSettings["SecurityProtocols.Add"]
        ?.Split(',')
        .Select(o => o.Trim())
        .ToArray();

    if (securityProtocolsAdd == null || securityProtocolsAdd.Length == 0)
    {
        return;
    }

    foreach (var securityProtocolString in securityProtocolsAdd)
    {
        SecurityProtocolType securityProtocolEnum;
        if (Enum.TryParse(securityProtocolString, out securityProtocolEnum))
        {
            // adds security protocol using binary operation
            ServicePointManager.SecurityProtocol |= securityProtocolEnum;
        }
    }
}

```

Here's a sample .NET Framework console application that sets the available security protocols to SSL3 only. This will intentionally fail to match security protocols when requesting 'swimburger.net':

```csharp
var originalSecurityProtocol = ServicePointManager.SecurityProtocol;
try
{
    Console.WriteLine("Request swimburger.net using SSL3, this should fail. Press any key to start request.");
    Console.ReadKey();
    ServicePointManager.SecurityProtocol = SecurityProtocolType.Ssl3;
    await httpClient.GetAsync("https://swimburger.net");
}
catch (HttpRequestException ex)
{
    Console.WriteLine(ex.ToString());
}

```

The console output will look like this:

```
Request swimburger.net using SSL3, this should fail. Press any key to start request.
System.Net.Http.HttpRequestException: An error occurred while sending the request. ---> System.Net.WebException: The underlying connection was closed: An unexpected error occurred on a receive. ---> System.ComponentModel.Win32Exception: The client and server cannot communicate, because they do not possess a common algorithm
   at System.Net.SSPIWrapper.AcquireCredentialsHandle(SSPIInterface SecModule, String package, CredentialUse intent, SecureCredential scc)
   at System.Net.Security.SecureChannel.AcquireCredentialsHandle(CredentialUse credUsage, SecureCredential& secureCredential)
   at System.Net.Security.SecureChannel.AcquireClientCredentials(Byte[]& thumbPrint)
   at System.Net.Security.SecureChannel.GenerateToken(Byte[] input, Int32 offset, Int32 count, Byte[]& output)
   at System.Net.Security.SecureChannel.NextMessage(Byte[] incoming, Int32 offset, Int32 count)
   at System.Net.Security.SslState.StartSendBlob(Byte[] incoming, Int32 count, AsyncProtocolRequest asyncRequest)
   at System.Net.Security.SslState.ForceAuthentication(Boolean receiveFirst, Byte[] buffer, AsyncProtocolRequest asyncRequest)
   at System.Net.Security.SslState.ProcessAuthentication(LazyAsyncResult lazyResult)
   at System.Net.TlsStream.CallProcessAuthentication(Object state)
   at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
   at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
   at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
   at System.Net.TlsStream.ProcessAuthentication(LazyAsyncResult result)
   at System.Net.TlsStream.BeginWrite(Byte[] buffer, Int32 offset, Int32 size, AsyncCallback asyncCallback, Object asyncState)
   at System.Net.TlsStream.UnsafeBeginWrite(Byte[] buffer, Int32 offset, Int32 size, AsyncCallback asyncCallback, Object asyncState)
   at System.Net.PooledStream.UnsafeBeginWrite(Byte[] buffer, Int32 offset, Int32 size, AsyncCallback callback, Object state)
   at System.Net.ConnectStream.WriteHeaders(Boolean async)
   --- End of inner exception stack trace ---
   at System.Net.HttpWebRequest.EndGetResponse(IAsyncResult asyncResult)
   at System.Net.Http.HttpClientHandler.GetResponseCallback(IAsyncResult ar)
   --- End of inner exception stack trace ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()
   at ChangeSecurityProtocols.Program.<Main>d\_\_1.MoveNext() in C:\Users\niels\source\repos\ChangeSecurityProtocols\Program.cs:line 22

```

As you can see by the output, an `HttpRequestException` is thrown and the inner inner exception is "System.ComponentModel.Win32Exception: The client and server cannot communicate, because they do not possess a common algorithm".

When you add TLS1.1 & 1.2 back by running `RemoveSecurityProtocols` then `AddSecurityProtocols`, the HTTP request succeeds:

```csharp
RemoveSecurityProtocols();
AddSecurityProtocols();

Console.WriteLine("Request swimburger.net with Tls11,Tls12 enabled, this should succeed. Press any key to start request.");
Console.ReadKey();
var response = await httpClient.GetAsync("https://swimburger.net");
Console.WriteLine($"Response: {response.StatusCode}");

```

The output looks like this:

```
Request swimburger.net with Tls11,Tls12 enabled, this should succeed. Press any key to start request.
Response: OK

```

Here's the entire console sample:

```csharp
using System;
using System.Configuration;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;

namespace ChangeSecurityProtocols
{
    class Program
    {
        private static HttpClient httpClient = new HttpClient();

        static async Task Main(string[] args)
        {
            var originalSecurityProtocol = ServicePointManager.SecurityProtocol;
            try
            {
                Console.WriteLine("Request swimburger.net using SSL3, this should fail. Press any key to start request.");
                Console.ReadKey();
                ServicePointManager.SecurityProtocol = SecurityProtocolType.Ssl3;
                await httpClient.GetAsync("https://swimburger.net");
            }
            catch (HttpRequestException ex)
            {
                Console.WriteLine(ex.ToString());
            }

            // update security protocols from appSettings
            RemoveSecurityProtocols();
            AddSecurityProtocols();

            Console.WriteLine("Request swimburger.net with Tls11,Tls12 enabled, this should succeed. Press any key to start request.");
            Console.ReadKey();
            var response = await httpClient.GetAsync("https://swimburger.net");
            Console.WriteLine($"Response: {response.StatusCode}");
            Console.WriteLine("Press any key to exit.");
            Console.ReadKey();
        }

        private static void RemoveSecurityProtocols()
        {
            var securityProtocolsToRemove = ConfigurationManager.AppSettings["SecurityProtocols.Remove"]
                ?.Split(',')
                .Select(o => o.Trim())
                .ToArray();

            if (securityProtocolsToRemove == null || securityProtocolsToRemove.Length == 0)
            {
                return;
            }

            foreach (var securityProtocolString in securityProtocolsToRemove)
            {
                SecurityProtocolType securityProtocolEnum;
                if (Enum.TryParse(securityProtocolString, out securityProtocolEnum))
                {
                    // removes security protocol using binary operation
                    ServicePointManager.SecurityProtocol &= ~securityProtocolEnum;
                }
            }
        }

        private static void AddSecurityProtocols()
        {
            var securityProtocolsAdd = ConfigurationManager.AppSettings["SecurityProtocols.Add"]
                ?.Split(',')
                .Select(o => o.Trim())
                .ToArray();

            if (securityProtocolsAdd == null || securityProtocolsAdd.Length == 0)
            {
                return;
            }

            foreach (var securityProtocolString in securityProtocolsAdd)
            {
                SecurityProtocolType securityProtocolEnum;
                if (Enum.TryParse(securityProtocolString, out securityProtocolEnum))
                {
                    // adds security protocol using binary operation
                    ServicePointManager.SecurityProtocol |= securityProtocolEnum;
                }
            }
        }
    }
}

```