由于以下错误消息,我们无法使用WebRequest连接到HTTPS服务器:

请求被中止:无法创建SSL/TLS安全通道。

我们知道服务器没有有效的HTTPS证书,但为了绕过这个问题,我们使用下面的代码,我们从另一个StackOverflow帖子:

private void Somewhere() {
    ServicePointManager.ServerCertificateValidationCallback += new RemoteCertificateValidationCallback(AlwaysGoodCertificate);
}

private static bool AlwaysGoodCertificate(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors policyErrors) {
   return true;
}

问题是服务器从未验证证书,并出现上述错误而失败。有人知道我该怎么做吗?


我应该提到的是,我和一个同事几周前进行了测试,它运行得很好,与我上面写的类似。我们发现的唯一“主要区别”是,我用的是Windows 7,而他用的是Windows XP。这会改变什么吗?


当前回答

这一个是为我工作在MVC webclient

public string DownloadSite(string RefinedLink)
{
    try
    {
        Uri address = new Uri(RefinedLink);

        ServicePointManager.ServerCertificateValidationCallback = delegate { return true; };
        ServicePointManager.SecurityProtocol = SecurityProtocolType.Ssl3;

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

        using (WebClient webClient = new WebClient())
        {
            var stream = webClient.OpenRead(address);
            using (StreamReader sr = new StreamReader(stream))
            {
                var page = sr.ReadToEnd();

                return page;
            }
        }

    }
    catch (Exception e)
    {
        log.Error("DownloadSite - error Lin = " + RefinedLink, e);
        return null;
    }
}

其他回答

在我的情况下,这个异常的根源是在代码的某些时候,下面的代码被调用:

ServicePointManager.SecurityProtocol = SecurityProtocolType.Ssl3;

这真的很糟糕。它不仅指示. net使用不安全的协议,而且还会影响在您的应用域中随后发出的每一个新的WebClient(和类似的)请求。(请注意,传入的web请求在你的ASP中不受影响。NET应用程序,但新的WebClient请求,如与外部web服务对话,是)。

在我的情况下,它实际上并不需要,所以我可以删除该语句,所有其他的web请求重新开始正常工作。根据我在其他地方的阅读,我了解到一些事情:

This is a global setting in your appdomain, and if you have concurrent activity, you can't reliably set it to one value, do your action, and then set it back. Another action may take place during that small window and be impacted. The correct setting is to leave it default. This allows .NET to continue to use whatever is the most secure default value as time goes on and you upgrade frameworks. Setting it to TLS12 (which is the most secure as of this writing) will work now but in 5 years may start causing mysterious problems. If you really need to set a value, you should consider doing it in a separate specialized application or appdomain and find a way to talk between it and your main pool. Because it's a single global value, trying to manage it within a busy app pool will only lead to trouble. This answer: https://stackoverflow.com/a/26754917/7656 provides a possible solution by way of a custom proxy. (Note I have not personally implemented it.)

没有一个答案对我有用。

这是有效的方法:

而不是像这样初始化我的X509Certifiacte2:

   var certificate = new X509Certificate2(bytes, pass);

我是这样做的:

   var certificate = new X509Certificate2(bytes, pass, X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.PersistKeySet | X509KeyStorageFlags.Exportable);

注意X509KeyStorageFlags。出口! !

我没有改变其余的代码(WebRequest本身):

// I'm not even sure the first two lines are necessary:
ServicePointManager.Expect100Continue = true; 
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;

request = (HttpWebRequest)WebRequest.Create(string.Format("https://{0}.sii.cl/cvc_cgi/dte/of_solicita_folios", server));
request.Method = "GET";
request.Referer = string.Format("https://hercules.sii.cl/cgi_AUT2000/autInicio.cgi?referencia=https://{0}.sii.cl/cvc_cgi/dte/of_solicita_folios", servidor);
request.UserAgent = "Mozilla/4.0";
request.ClientCertificates.Add(certificate);
request.CookieContainer = new CookieContainer();

using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())
{
    // etc...
}

事实上,我甚至不确定前两行是否有必要……

我在Windows 2008服务器上的. net 4.5.2 Winform应用程序上得到了相同的错误。

我尝试了以下修复方法:

ServicePointManager。SecurityProtocol = SecurityProtocolType. tls1 |安全协议类型。Tls11 | SecurityProtocolType.Tls12;

但这并没有起作用,错误的发生次数仍然存在。

根据上面的答案之一,是否必须覆盖注册表的SchUseStrongCrypto键。如果我设置这个键会有什么副作用吗?

Windows Registry Editor Version 5.00
[HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\.NETFramework\v4.0.30319]
"SchUseStrongCrypto"=dword:00000001
[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\.NETFramework\v4.0.30319]
"SchUseStrongCrypto"=dword:00000001

这是原始答案所没有的。我又加了些代码让它防弹。

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

注意:这里投票最多的几个答案建议设置ServicePointManager。安全协议,但微软明确建议不要这样做。下面,我将讨论此问题的典型原因以及解决该问题的最佳实践。

这个问题的最大原因之一是活动的。net框架版本。. net框架运行时版本会影响默认启用的安全协议。

在ASP。NET站点,框架运行时版本通常在web.config中指定。(见下文) 在其他应用程序中,运行时版本通常是构建项目的版本,而不管它是否运行在具有更新的. net版本的机器上。

似乎没有任何权威的文档说明它在不同版本中是如何具体工作的,但似乎默认值或多或少是这样确定的:

Framework Version Default Protocols
4.5 and earlier SSL 3.0, TLS 1.0
4.6.x TLS 1.0, 1.1, 1.2, 1.3
4.7+ System (OS) Defaults

对于较旧的版本,根据系统上安装的. net运行时的不同,您的里程可能会有所不同。例如,可能会出现这样的情况,您正在使用一个非常旧的框架,不支持TLS 1.0,或者使用4.6。不支持TLS 1.3。

微软的文档强烈建议使用4.7+和系统默认值:

我们建议您: 在你的应用程序上瞄准。net Framework 4.7或更高版本。在你的WCF应用中瞄准。net Framework 4.7.1或更高版本。 不指定TLS版本。配置代码,让操作系统决定TLS版本。 执行彻底的代码审计,以验证您没有指定TLS或SSL版本。

ASP。NET站点:检查<httpRuntime>元素中的targetFramework版本,因为这个(当出现时)决定了你的站点实际使用的运行时:

<httpRuntime targetFramework="4.5" />

好:

<httpRuntime targetFramework="4.7" />