WebRequest错误 - 无法创建SSL / TLS安全通道

时间:2017-01-10 19:45:06

标签: c# ssl https

我正在尝试编写C#代码,该代码针对用于计算Web应用程序中的销售税的REST服务端点发出Web请求。这是第三方服务,使用SSL进行保护。有两种环境,UAT和生产。运行webrequest的代码如下所示:

...

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;

这对UAT环境很好。但是当我在生产环境中运行相同的代码时,我收到错误:

“无法创建SSL / TLS安全通道”

能够毫无问题地执行Postman的两个请求,无需任何特殊修改。

这导致我走上了调查此错误的道路,我发现许多有用的SO帖子讨论了这个主题,包括:

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

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

这些帮助我指出了正确的方向,即将ServicePointManager.SecurityProtocol设置设置为不同的值,并使用ServicePointManager.ServerCertificateValidationCallback来调查错误。

在玩这些之后我发现了以下内容:

  • UAT环境调用将使用默认设置 Ssl3 | Tls (.NET 4.5.2的默认设置),而生产环境则不会。
  • 只有当我将此设置明确设置为 Ssl3 时,制作调用才有效。

该代码如下所示:

...

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;

这尤其令人困惑,因为在查看Web浏览器中的端点时,我可以看到它们都受到相同通配符证书的保护,并且都使用TLS 1.0。

所以我希望将ServicePointManager.SecurityProtocol设置为TLS可以正常工作,但事实并非如此。

我真的想避免将ServicePointManager.SecurityProtocol明确地设置为SSL3,因为我们的应用程序是一个Web应用程序,并且有多个其他通过SSL进行通信的集成点。这些都很好,我不想对它们的功能产生负面影响。即使我在调用之前设置了此设置,然后立即将其更改回来,我也有可能遇到并发问题,因为ServicePointManager.SecurityProtocol是静态的。

我也调查了这个话题,并且不喜欢我读的内容。有人提到使用不同的应用程序域:

.NET https requests with different security protocols across threads

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

但这对我来说似乎过于复杂/苛刻。处理创建应用程序域真的是唯一的解决方案吗?或者这是我不应该试图解决的问题,而是与相关服务的所有者一起讨论?我非常好奇它可以在一个环境/服务器上使用TLS,但不能在另一个环境/服务器上使用。

修改 我做了更多的玩这个。我更改了我的客户端以使用此博客文章中概述的方法(使用不同的应用程序域来隔离更改ServicePointManager.SecurityProtocol的代码):

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

这实际上运作得很好,可能是后退解决方案。但我也了解到有问题的服务提供商有一个使用TLS 1.2保护的不同端点(相同的URL,不同的端口)。值得庆幸的是,通过在global.asax.cs应用程序启动事件中扩展我的SecurityProtocol设置:

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

我能够在所有环境中与服务进行良好沟通。它也不会影响我与其他服务的现有集成(例如,Cyber​​Source)。

然而 - 现在有一个新的但相关的问题。 为什么如果我如上所述展开SecurityProtocolType,这个调用是否有效?我的其他集成,如Cyber​​Source,并不需要这样。但是这个确实如此。并且它们似乎都是使用我在浏览器中看到的TLS 1.2保护的。

3 个答案:

答案 0 :(得分:1)

如果你运行4.0,你可以像这样使用它:

ServicePointManager.SecurityProtocol = (SecurityProtocolType)3072; // SecurityProtocolType.Tls12

答案 1 :(得分:1)

这个对我有用:

ServicePointManager.Expect100Continue = true;
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls
                   | SecurityProtocolType.Tls11
                   | SecurityProtocolType.Tls12
                   | SecurityProtocolType.Ssl3;

答案 2 :(得分:0)

隐藏了Web服务器上的某些高级TLS配置。您的生产服务器可能已被修改,以防止DROWN,logjam,FREAK,POODLE和BEAST攻击。

要更改这些高级TLS设置,它不像单击IIS中的某些按钮那么简单。如果你使用像这样的第三方工具,那就简单了:https://www.nartac.com/Products/IISCrypto

这些配置适用于主要的最新Web浏览器,但.Net似乎很难使用这种现代安全服务器配置(除非您手动覆盖默认设置)。

结论:这并不明显,你的UAT和制作环境看起来是一样的,但它们不是。