Рекомендации по использованию ServerCertificateValidationCallback

Я работаю над проектом, который использует некоторую HTTP-связь между двумя серверными серверами. Серверы используют сертификаты X509 для аутентификации. Излишне говорить, что когда сервер A (клиент) устанавливает соединение с сервером B (сервер), существует ошибка проверки SSL/TLS, поскольку используемые сертификаты не принадлежат доверенным сторонним организациям.

Обычно способ обращения с ним - это ServicePointManager.ServerCertificateValidationCallback, например:

ServicePointManager.ServerCertificateValidationCallback += 
        (sender, cert, chain, error) =>
{
    return cert.GetCertHashString() == "xxxxxxxxxxxxxxxx";
};

Этот подход работает, за исключением того, что он не идеален. По сути, это процедура переопределения проверки для КАЖДОГО HTTP-запроса, выполняемого приложением. Таким образом, если другой класс попытается запустить HTTP-запрос, он не удастся. Кроме того, если другой класс переопределяет ServicePointManager.ServerCertificateValidationCallback для своих целей, то мое сообщение начинает выходить из строя внезапно.

Единственное решение, которое приходит на ум, создает отдельный AppDomain для выполнения клиентских HTTP-запросов. Это будет работать, но на самом деле - глупо делать это только для того, чтобы можно было выполнять HTTP-запросы. Накладные расходы будут ошеломляющими.

С учетом этого, кто-нибудь исследовал, есть ли в .NET более эффективная практика, которая позволила бы получать доступ к веб-службам при обработке проверки SSL/TLS клиента, не затрагивая других веб-клиентов?

Ответ 1

Допустимой (безопасной) методологией, работающей в .NET 4.5+, является использование HttpWebRequest.ServerCertificateValidationCallback. Назначение этого обратного вызова для конкретного экземпляра запроса изменяет логику проверки только для запроса, не влияя на другие запросы.

var request = (HttpWebRequest)WebRequest.Create("https://...");
request.ServerCertificateValidationCallback += 
        (sender, cert, chain, error) =>
{
    return cert.GetCertHashString() == "xxxxxxxxxxxxxxxx";
};

Ответ 2

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

Ответ 3

Альтернатива для кода, который не использует HttpWebRequest, и для сред, где вы не можете установить доверенные сертификаты в хранилище сертификатов: Проверьте параметр ошибки обратного вызова, который будет содержать любую ошибку, обнаруженную до обратного вызова. Таким образом, вы можете игнорировать ошибки для конкретных хэш-строк, но при этом принимать другие сертификаты, которые проходят проверку.

ServicePointManager.ServerCertificateValidationCallback += 
    (sender, cert, chain, error) =>
{
    if (cert.GetCertHashString() == "xxxxxxxxxxxxxxxx")
    {
        return true;
    }
    else
    {
       return error == SslPolicyErrors.None;
    }
};

Ссылка: https://msdn.microsoft.com/en-us/library/system.net.security.remotecertificatevalidationcallback(v=vs.110).aspx

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