由于以下错误消息,我们无法使用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。这会改变什么吗?


当前回答

在Windows Server 2012中,从注册表中删除这个选项对我有帮助 [HKEY_LOCAL_MACHINE \ SYSTEM \ CurrentControlSet \ \ SecurityProviders \ SCHANNEL \ KeyExchangAlgorithms控制

其他回答

这个错误是一般的,有很多原因导致SSL/TLS协商失败。最常见的是无效或过期的服务器证书,您可以通过提供自己的服务器证书验证钩子来解决这个问题,但这并不一定是唯一的原因。服务器可能需要相互身份验证,它可能配置了一组客户端不支持的密码,它可能有太大的时间漂移,以至于握手无法成功,还有很多其他原因。

最好的解决方案是使用channel故障排除工具集。channnel是负责SSL和TLS的SSPI提供者,您的客户端将使用它进行握手。看看TLS/SSL工具和设置。

另请参阅如何启用通道事件日志记录。

在。net 4.5中,这个问题的解决方案是

ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;

如果你没有。net 4.5,那就使用

ServicePointManager.SecurityProtocol = (SecurityProtocolType)3072;

对于SOAP/WCF用户,当服务器拒绝您的WS-Security配置时,也会发生此错误。客户端接收到的错误信息非常模糊,但是服务器管理员可能能够确定原因。

UsernameToken配置文件下有一个这样的例子,其中消息被<wsu:Created> time视为过期,不是有效的ISO 8601 datetime,原因可能是格式错误、不是UTC或服务器时间不匹配。

<wsse:UsernameToken wsu:Id="Example-1">
   <wsse:Username> ... </wsse:Username>
   <wsse:Password Type="..."> ... </wsse:Password>
   <wsse:Nonce EncodingType="..."> ... </wsse:Nonce>
   <wsu:Created>2021-01-31T19:00:00.0000000Z</wsu:Created>
</wsse:UsernameToken>

这样做帮助了我:

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

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

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.)