0

Учитывая: два консольных приложения C# на разных машинах. Один из них - клиент, один - сервер. Клиент использует HttpClient.SendAsync для отправки запроса на сервер.C# request.GetClientCertificate() зависает при обработке запроса PUT/POST с большей полезной нагрузкой

Каждый запрос, отправленный на сервер, имеет сертификат клиента, прикрепленный к обработчику запроса, и каждый раз, когда сервер получает сертификат для проверки с помощью GetClientCertificate().

Обычно все работает нормально: все вызовы GET, а также вызовы PUT с небольшой полезной нагрузкой (~ 4kb) последовательно проходят.

Однако, если вы выполняете запрос PUT с большей полезной нагрузкой (например, ~ 40kb), то GetClientCertificate() вызывается в блоке на стороне сервера и не возвращается до тех пор, пока через 2 минуты (это наш период ожидания). Возвращенный сертификат имеет значение null. Клиент сообщает об ошибке «не может установить канал SSL/TLS».

Похоже, что GetClientCertificate() делает некоторые дополнительные сообщения, которые я не понимаю, и хотел бы получить некоторую информацию о том, что может быть тайм-аут. Например, мы попытались отключить проверку отзыва сертификатов на сервере, но это не помогло. Кроме того, мне интересно, может ли какой-то промежуточный узел сети отказаться от этого сертификата, и как это можно диагностировать.

Кроме того, эта ошибка не происходит последовательно. Иногда он может работать несколько десятков раз, но когда он начинает проваливаться, он будет продолжать работать. Это может указывать на утечки ресурсов в какой-то степени, и было бы неплохо узнать, что мы могли бы утечка в этом сценарии. Мы утилизируем http-клиент, обработчики и закрываем хранилище сертификатов.

Я видел подобные вопросы в StackOverflow, где проблема заключалась в том, что ASP.NET требует настройки, ожидая НЕ возобновления в захваченном контексте. Мне интересно, могут ли подобные проблемы быть применимы к консольным приложениям.

Благодарим за помощь!

+0

Вам действительно нужно рассказать нам больше о вашем сервере. Непонятно, как вы обслуживаете, хотя можно предположить, что это может быть через «HttpListener». Угадает, что вы хотите? – spender

+0

@ spender, спасибо, я должен был это предоставить.Я хотел задать вопрос как можно более конкретным и сосредоточиться на GetClientCertificate, не заставляя вас читать ненужный код, но теперь я вижу вашу точку зрения. Мы используем OwinHttpListener, а затем Owin вызывает WebApp.Start(). Код довольно большой и трудно вставить таким образом, чтобы это имело смысл, но если вам нужны некоторые данные о том, как выполняются конкретные шаги или какие настройки конфигурации, я обязательно их предоставил. – user2520968

ответ

2

Прежде чем позвонить GetClientCertificate(), вам необходимо прочитать данные POST.

Кроме того, для вас может быть возможность включения согласования сертификатов клиента на привязку SSL-сертификата сервера (но это приведет к тому, что каждый запрос попросит об этом).

Запустите netsh http show sslcert на своем сервере и ищите сертификат, связанный с данным портом, и посмотрите на Согласование сертификата клиента. Если вы измените это на Включено, тогда он будет вызывать согласование сертификата клиента в начале каждого соединения, поэтому к моменту, когда ваш код вызывает 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. 
} 
1

Просьба ознакомиться с этим article. У этого есть причина и рекомендации для обработки таких случаев. Короче говоря, предложение Кевина о чтении тела перед чтением сертификата клиента должно решить проблему.