C# request.GetClientCertificate() 在处理具有较大负载的 PUT/POST 请求时冻结

C# request.GetClientCertificate() freezes when processing PUT/POST request with larger payload

给定:两个 C# 控制台应用程序,位于不同的机器上。一个是客户端,一个是服务器。客户端使用 HttpClient.SendAsync 向服务器发送请求。

发送到服务器的每个请求都有一个附加到请求处理程序的客户端证书,每次服务器使用 GetClientCertificate() 获取证书进行验证时。

通常一切正常:所有 GET 调用以及负载较小 (~4kb) 的 PUT 调用始终如一地通过。

但是,当您使用更大的负载(例如 ~40kb)执行 PUT 请求时,服务器端的 GetClientCertificate() 调用会阻塞并且 return 直到 2 分钟后(即我们的超时时间)。证书 returned 为空。客户端报错"cannot establish SSL/TLS channel."

似乎 GetClientCertificate() 进行了一些我不理解的额外通信,并且想了解一些关于什么可能超时的信息。例如,我们曾尝试禁用服务器上的证书吊销检查,但这没有帮助。另外,我想知道某些中间网络节点是否有可能丢弃该证书,以及如何诊断这种情况。

此外,此错误不会一直发生。有时它可以工作几十次,但当它开始失败时,它会一直失败。这可能表明某种资源泄漏,很高兴知道在这种情况下我们可能会泄漏什么。我们确实处理了 http 客户端、处理程序,并关闭了证书存储。

我在 Whosebug 上看到过类似的问题,其中的问题是 ASP.NET 需要配置等待以不在捕获的上下文中恢复。我想知道任何类似的问题是否适用于控制台应用程序。

感谢您的帮助!

调用GetClientCertificate()前需要先读取POST数据。

此外,在服务器的 SSL 证书绑定上启用客户端证书协商可能是您的一个选项(但它会导致每个请求都要求一个)。

运行 netsh http show sslcert 在您的服务器上查找绑定到相关端口的证书并查看 协商客户端证书 属性。如果您将其更改为 Enabled,那么它将在每个连接开始时调用客户端证书协商,因此在您的代码调用 GetClientCertificate() 时,它已经存在。

这里有一些代码可以在访问客户端证书之前读取 POST 数据。在我的测试中,这可靠地工作,但正如我在评论中指出的那样,可能并不适合所有用例。

private static void ProcessPostRequest(System.Net.HttpListenerContext context)
{
    // Read the entire POST data into a byte array.  This isn't ideal.  Also, it does not supported 'chunked' encoding.
    byte[] input      = new byte[context.Request.ContentLength64];
    int    bytesRead  = 0;
    int    totalBytes = 0;

    while ((bytesRead = context.Request.InputStream.Read(input, totalBytes, Math.Min(4096, input.Length - totalBytes))) > 0)
    {
        totalBytes += bytesRead;
    }

    System.Diagnostics.Debug.WriteLine($"Read {totalBytes:#,##}");

    var cert = context.Request.GetClientCertificate();

    if (cert != null)
    {
        System.Diagnostics.Debug.WriteLine(cert.Subject);
    }

    // Be sure to write to or close the Response.
}

请阅读这篇文章article。 它有处理此类案件的理由和建议。简而言之,凯文建议在阅读客户端证书之前阅读正文应该可以解决问题。