Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

WebRequest Error - Could not create SSL/TLS secure channel

Tags:

c#

https

ssl

I am trying to write C# code which makes a web request against a REST service endpoint used for calculating sales tax within a web application. This is a third party service, and it is secured using SSL. There are two environments, UAT and production. The code that runs the webrequest looks like this:

...

var req = WebRequest.Create(url) as HttpWebRequest;
req.Method = "POST";
req.ContentType = "application/json";

...

using (var webresponse = req.GetResponse())
{
    using (var responseStream = new StreamReader(webresponse.GetResponseStream()))
    {
        var respJson = responseStream.ReadToEnd();
        calcResult = BuildResponse(calcRequest, respJson, consoleWriteRawReqResponse);
    }
}

return calcResult;

This works fine against the UAT environment. But when I run the same code against the production environment, I get the error:

"Could not create SSL/TLS secure channel"

I am able to execute both requests from Postman without issue, without any special modifications.

This led me down the path of investigating this error, and I found many helpful SO posts discussing the topic, including:

The request was aborted: Could not create SSL/TLS secure channel

Could not create SSL/TLS secure channel, despite setting ServerCertificateValidationCallback

These helped by pointing me in the right direction, which was to look at setting the ServicePointManager.SecurityProtocol setting to a different value, and using the ServicePointManager.ServerCertificateValidationCallback to investigate errors.

What I found after playing with these is the following:

  • The UAT environment call will work with the default setting of Ssl3 | Tls (default for .NET 4.5.2), while the production environment will not.
  • The production call will work ONLY when I set this setting to explicitly to Ssl3.

That code looks like this:

...

var req = WebRequest.Create(url) as HttpWebRequest;
req.Method = "POST";
req.ContentType = "application/json";

...

ServicePointManager.SecurityProtocol = SecurityProtocolType.Ssl3;
ServicePointManager.ServerCertificateValidationCallback = new RemoteCertificateValidationCallback(CertValidationCallback);
using (var webresponse = req.GetResponse())
{
    using (var responseStream = new StreamReader(webresponse.GetResponseStream()))
    {
        var respJson = responseStream.ReadToEnd();
        calcResult = BuildResponse(calcRequest, respJson, consoleWriteRawReqResponse);
    }
}
ServicePointManager.SecurityProtocol = SecurityProtocolType.Ssl3 | SecurityProtocolType.Tls;

return calcResult;

This is particularly confusing because in looking at the endpoints in a web browser, I can see that they are both secured by the same wildcard certificate and are both using TLS 1.0.

So I would expect that setting ServicePointManager.SecurityProtocol to TLS would work, but it does not.

I really want to avoid setting ServicePointManager.SecurityProtocol explicitly to SSL3 because our application is a web application and has multiple other integration points that communicate over SSL. These are all working fine and I do not want to adversely affect their functionality. Even if I set this setting right before the call, and then change it back right after, I run the risk of hitting concurrency issues since ServicePointManager.SecurityProtocol is static.

I investigated that topic as well, and did not like what I read. There are mentions of using different app domains:

.NET https requests with different security protocols across threads

How to use SSL3 instead of TLS in a particular HttpWebRequest?

But that seems overly complex / hacky to me. Is dealing with creating an app domain really the only solution? Or is this something I simply should not be trying to solve and instead take it up with the owner of the service in question? It is very curious to me that it would work with TLS on one environment / server, but not the other.

EDIT I did some more playing with this. I changed my client to use the approach outlined quite well in this blog post (using a different app domain to isolate the code that changes the ServicePointManager.SecurityProtocol):

https://bitlush.com/blog/executing-code-in-a-separate-application-domain-using-c-sharp

This actually worked quite well, and could be fall back solution. But I also learned that the provider of the service in question has a different endpoint (same URL, different port) that is secured using TLS 1.2. Thankfully, by expanding my SecurityProtocol setting like so in the global.asax.cs application start event:

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

I am able to communicate with the service fine in all environments. It also does not affect my existing integrations with other services (CyberSource, for example).

However - now there is a new but related question. Why is it that this call ONLY works if I expand SecurityProtocolType as above? My other integrations, like CyberSource, did not require this. Yet this one does. And they all appear to be secured using TLS 1.2 from what I saw in the browser.

like image 936
exzachtly1 Avatar asked Jan 10 '17 19:01

exzachtly1


People also ask

Can not create SSL TLS secure channel?

The error “The request was aborted: Could not create SSL/TLS secure channel.” can happen during any download HTTP request. This error generally will correspond to firewalls, proxies or DNS filtering blocking the connection or an SSL/TLS cipher misconfiguration.

Can't create SSL TLS secure channel IIS?

However, the "Could not create SSL/TLS secure channel" error usually means that there's something wrong with the server certificate, e.g. the certificate is for a different hostname, or otherwise invalid, or not trusted etc.

What is a SSL TLS secure channel?

SSL/TLS creates a secure channel between a users' computer and other devices as they exchange information over the internet, using three main concepts: encryption, authentication, and integrity to accomplish this. Encryption hides data being transferred from any third parties.

How do I find TLS version in Windows?

1. Click on: Start -> Control Panel -> Internet Options 2. Click on the Advanced tab 3. Scroll to the bottom and check the TLS version described in steps 3 and 4: 4.


2 Answers

If you run 4.0 you can use it like this:

ServicePointManager.SecurityProtocol = (SecurityProtocolType)3072; // SecurityProtocolType.Tls12
like image 87
Rafael Pereira Avatar answered Oct 13 '22 03:10

Rafael Pereira


This one worked for me:

ServicePointManager.Expect100Continue = true;
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls
                   | SecurityProtocolType.Tls11
                   | SecurityProtocolType.Tls12
                   | SecurityProtocolType.Ssl3;
like image 42
Annia Martinez Avatar answered Oct 13 '22 04:10

Annia Martinez