How to specify SSL protocol to use for WebClient class
Asked Answered
A

2

45

I have an application that sends data to a server using an HTTPS POST. I use a System.Net.WebClient object to do this. Here is a function that sends some data:

    private byte[] PostNameValuePairs(string uri, NameValueCollection pairs)
    {
        byte[] response;
        String responsestring = "";
        using (WebClient client = new WebClient())
        {
            client.Headers = GetAuthenticationHeader();

            string DataSent = GetNameValueCollectionValuesString(pairs);

            try
            {
                response = client.UploadValues(uri, pairs);
                responsestring = Encoding.ASCII.GetString(response);
            }
            catch (Exception e)
            {
                responsestring = "CONNECTION ERROR: " + e.Message;
                return Encoding.ASCII.GetBytes(responsestring);
            }
            finally
            {
                _communicationLogger.LogCommunication(uri, client.Headers.ToString(), DataSent, responsestring);
            }
        }

        return response;
    }

We are passing in a URI beginning with https://

This has been working great for a long time. Today, we started getting the following connection error: "The underlying connection was closed: An unexpected error occurred on a send". We did some troubleshooting with the owner of the server, and they finally narrowed it down to the following. They made a change to their server to block TLS 1.0, and said that we now need to send our data using either TLS 1.1 or 1.2.

What do I need to set in my WebClient object (or elsewhere in my function) to make it use TLS 1.1 or 1.2 instead of TLS 1.0?

We are using .NET Framework 4.5 if that makes a difference.

Anandrous answered 27/5, 2015 at 19:42 Comment(4)
@CodeCaster, while the answer might be the same as this question, it's not clear it's a duplicate. There's no mention of WebClient at all in that other Q&A, so explaining how these classes are related could be useful, and the asker and other readers might also be interested in potential solutions for setting this on a per-instance basis, for example.Oestradiol
Depending on the platform you're using, you might be interested in this other question too.Oestradiol
@Oestradiol sure, but that too has been answered before. I couldn't find a proper one though, and the dupe I selected explains it all pretty well. This reopened question is now prone to "use System.Net.ServicePointManager.SecurityProtocol" answers without any proper explanation in them, as there are already hundreds of. And according to Set the SecurityProtocol (Ssl3 or TLS) on the .net HttpWebRequest per request, you can't set it per request. WebClient uses HttpWebRequest internally anyway.Horrid
Not being able to set the SecurityProtocol per request is definitely a possible issue, especially if you're connecting to different servers - there's a discussion of that here, along with some code explaining how we got around it: #26160618Ride
A
86

From the suggested other questions, I was able to solve it by adding the following line to my code:

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

This disabled TLS 1.0 from the client, and then the server accepted the connection.

Hope this helps someone else with the same issue. Although the answer is similar to those other questions, it wasn't obvious from the questions asked that this was the case, so I don't feel that this is a duplicate.

Anandrous answered 1/7, 2015 at 20:44 Comment(5)
The code you shared is for .NET 4.0 applications or newer. For older .NET apps, refer to https://mcmap.net/q/67081/-how-to-implement-security-protocols-tls-1-2-in-net-3-5-frameworkTracytrade
If you OR the TLS11 and TLS12 enumeration values, does this not mean you allow BOTH rather than disabling the former?Sneer
@Sneer Yes, it will allow both TLS11 and TLS12. The point was to disable TLS10.Anandrous
What about setting this for one single instance of WebClient? I have years of things set up and have no idea if any of them will break if I set this globally.Purtenance
@Purtenance if you use |= instead of \ it will add in the protocols you specify while keeping the old ones also. For example: ServicePointManager.SecurityProtocol |= SecurityProtocolType.Tls12;. See also Gwasshoppa's answerParry
U
4

I have found a slightly more strict version of switching on and off TLS versions in c#.

This can be used with .Net 4.5 and above.

// Remove insecure protocols (SSL3, TLS 1.0, TLS 1.1)
ServicePointManager.SecurityProtocol &= ~SecurityProtocolType.Ssl3;
ServicePointManager.SecurityProtocol &= ~SecurityProtocolType.Tls;
ServicePointManager.SecurityProtocol &= ~SecurityProtocolType.Tls11;
// Add TLS 1.2
ServicePointManager.SecurityProtocol |= SecurityProtocolType.Tls12;

This makes sure that even if the server is able to use 1.0 or 1.1 we can specifically exclude this from occurring.

Unless answered 12/6, 2020 at 3:11 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.