简体   繁体   中英

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

Given: two C# console apps, on different machines. One is a client, one is a server. The client uses HttpClient.SendAsync to send request to the server.

Every request sent to the server has a client certificate attached to the request handler and each time the server gets the cert for validation using GetClientCertificate().

Normally everything works fine: all the GET calls as well as PUT calls with small payload (~4kb) consistently go through.

However, when you do a PUT request with a larger payload (eg. ~40kb), then GetClientCertificate() call on the server side block and doesn't return until after 2 minutes (which is our timeout period). The cert returned is null. The client reports an error "cannot establish SSL/TLS channel."

It appears that GetClientCertificate() does some extra communication that I don't understand, and would like some information about to know what could be timing out. For example, we have tried disabling the cert revocation check on the server, but that didn't help. Also, I'm wondering if it's possible for some intermediate network node to drop that certificate, and how can this be diagnosed.

Also, this error doesn't happen consistently. Sometimes it can work for a few dozen times, but when it starts failing, and it will keep failing. This may indicate resource leaks of some sort, and it would nice to know what we could be leaking with this scenario. We do dispose of http client, handlers, and close the certificate store.

I've seen similar questions on StackOverflow where the problem was that ASP.NET requires configuring await to NOT resume on a captured context. I'm wondering if the any similar concerns could be applicable to console apps.

Thanks for your help!

You need to read the POST data first before calling GetClientCertificate() .

Also, enabling client certificate negotiation on the server's SSL certificate binding may be an option for you (but it will cause every request to ask for one).

Run netsh http show sslcert on your server and look for the certificate that is bound to the port in question and look at the Negotiate Client Certificate property. If you change that to Enabled , then it will invoke client certificate negotiation at the beginning of each connection, so by the time your code calls GetClientCertificate() , it will already be there.

Here is some code that will read the POST data before accessing the client certificate. In my tests, this works reliably, but as I noted in the comment may not be ideal for all use cases.

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

Please read this article . It has the reason and recommendations for handling such cases. In short, Kevin's suggestion of reading the body before reading the client certificate should solve the problem.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM