C#和dotnet 4.7.1无法为TLS 1.2调用添加自定义证书

19
我有以下的C#代码,用于构建一个带有自定义证书的https调用。当使用Tls 1.1时,调用可以正常工作。但是当使用Tls 1.2时,调用会中断。我使用curl,使用tls 1.2也能正常工作。
C# 代码:
X509Certificate2Collection collection = new X509Certificate2Collection();
collection.Import("C:\\SomePath\\MyCertificate.pfx", "MyPassword", X509KeyStorageFlags.PersistKeySet);
var cert = collection[0];

ServicePointManager.SecurityProtocol = ...;

ServicePointManager.ServerCertificateValidationCallback += (sender, certificate, chain, errors) => true;
HttpClientHandler handler = new HttpClientHandler();
handler.ServerCertificateCustomValidationCallback = (message, certificate2, arg3, arg4) => true;
handler.ClientCertificates.Add(cert);

var content = new ByteArrayContent(Encoding.GetEncoding("latin1").GetBytes("Hello world"));
HttpClient client = new HttpClient(handler);
var resp = client.PostAsync(requestUri: url, content: content).Result.Content.ReadAsStringAsync().Result;

适用于:

ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls11;

出现错误:

ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;

.Net错误信息:SocketException: 远程主机强制关闭了一个现有的连接

.Net版本:4.7.1

操作系统:Windows 10版本1703(支持的密码列表:https://msdn.microsoft.com/en-us/library/windows/desktop/mt808163(v=vs.85).aspx) - 服务器指定使用TLS_RSA_WITH_AES_256_GCM_SHA384,这是受支持的密码之一。

在wireshark中,我可以看到在工作的调用中(C# / Tls 1.1和Curl Tls 1.2),证书被发送到服务器。这是C# tls 1.1调用的wireshark转储:

Wireshark dump - Csharp tls 1.1

然而,在wireshark中,我也可以看到使用C# / Tls 1.2时,客户端未向服务器发送证书。这是C# tls 1.2调用的wireshark转储:

enter image description here

有人能看出我错过了什么吗?

更新

看起来证书具有不受Windows中的Schannel支持的md5签名,与tls 1.2结合使用。我们的供应商为我们创建了另一个证书作为解决方案。

我遇到了这个随机的线程,讨论了这个问题:https://community.qualys.com/thread/15498


我曾经遇到过类似的问题,是在使用 .Net Core 2.0 客户端/服务器应用程序时。我通过在服务器上强制使用 TLS 1.2 来解决了这个问题。这样一来,客户端就可以正确地协商协议和证书了。你可以尝试连接到一个只强制使用 TLS 1.2 的服务器上,并观察是否会出现与 .NET Framework 相同的行为。 - Ghigo
在这里更改服务器并不是一个真正的选项,因为服务器属于供应商。而且,即使我添加了tls 1.1,也没有关系。只要tls 1.2是允许的协议之一,就会出现这个问题。我需要知道为什么.net会出现这种情况,而curl却可以正常工作。而且我需要启用tls 1.2,因为这是一个静态设置,其他连接也依赖于同一应用程序中的tls 1.2。如果我可以关闭这个单独调用的tls 1.3,那就可以了。但是,在处理程序对象上设置协议会抛出一个异常,告诉我们在当前环境中无法实现这一点。 - Stephan Møller
1
看起来很像这个问题的重复 https://dev59.com/QVcP5IYBdhLWcg3wL3YN - Robbert Draaisma
2
我不认为这是重复的。首先,您的链接显示了一个描述tls 1.2不起作用的线程,除非显式设置 - 我的问题有点相反:启用tls 1.2时https不起作用。其次,使用app.config描述的修复程序没有帮助任何东西。 - Stephan Møller
你的curl参数是什么?此外,你必须使用Wireshark来对抗Curl...你确定它正在以TLS 1.2传递吗? - Ctznkane525
显示剩余3条评论
3个回答

2

您对这个问题的根本原因是正确的:默认情况下,基于schannel的客户端提供SHA1、SHA256、SHA384和SHA512(在Win10/Server 2016上)。因此,TLS 1.2服务器不应该向这些客户端发送其MD5证书。

客户端(HttpClient)在signature_algorithms扩展中未列出MD5,因此TLS 1.2握手失败。解决方法是使用安全的服务器证书。


0

你不应该指定处理程序的SslProtocols属性吗?

尝试在handler定义之后添加这行:

handler.SslProtocols = SslProtocols.Tls12;

0

我相信这段代码通过始终盲目地返回true来掩盖某种证书错误:

handler.ServerCertificateCustomValidationCallback = (message, certificate2, arg3, arg4) => true;

我建议您编写一个函数来真正分析arg4的结果。那是您的SSL策略错误。记录它们,您将得到答案。在我的示例中,我写入控制台,但您可以写入跟踪或文件。您将获得一个数字,该数字将与SslPolicyErrors枚举的值相关联。根据结果,您可能需要检查您的arg3,即您的链。
ServicePointManager.ServerCertificateValidationCallback += (sender, cert, chain, sslPolicyErrors) => {

SslPolicyErrors errs = sslPolicyErrors;
Console.WriteLine("Policy Errors " + sslPolicyErrors.ToString());           
return true;};

实际上,即使在使用tls 1.2时没有被调用,但在使用tls 1.1时确实被调用,并指定错误类型“System.Net.Security.SslPolicyErrors.RemoteCertificateChainErrors”。该方法返回true,允许在tls 1.1中调用,但由于更高的安全标准,可能根本不允许在tls 1.2中进行此操作。我最好的选择是找出为什么tls 1.1会发生链错误,然后修复错误并再次尝试tls 1.2。我会带着结果回来的。 - Stephan Møller
在深入研究回调方法提供的对象后,我发现这个消息深藏其中:“证书链已处理,但终止于一个根证书,该证书未被信任提供者信任。” - 我将专门搜索此错误。 - Stephan Møller
你是从一个合法的公司购买了证书还是自己制作了一个...自己制作的证书只适用于内部使用。 - Ctznkane525
这可能有助于第二个错误 https://dev59.com/GGgu5IYBdhLWcg3wkH6P - Ctznkane525
我从供应商手中拿到了自定义证书。这是否应该依赖于可信的根证书呢? - Stephan Møller
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接