Лучшие практики для использования 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 клиента, не затрагивая другие веб-клиенты?

3 ответа

Решение

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

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

Альтернатива для кода, который не использует 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

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

Я знаю, что немного опоздал на вечеринку, но другой вариант - использовать класс, наследующий IDisposable что можно поместить в using(){} блокировать свой код:

public class ServicePointManagerX509Helper : IDisposable
{
    private readonly SecurityProtocolType _originalProtocol;

    public ServicePointManagerX509Helper()
    {
        _originalProtocol = ServicePointManager.SecurityProtocol;
        ServicePointManager.ServerCertificateValidationCallback += TrustingCallBack;
    }

    public void Dispose()
    {
        ServicePointManager.SecurityProtocol = _originalProtocol;
        ServicePointManager.ServerCertificateValidationCallback -= TrustingCallBack;
    }

    private bool TrustingCallBack(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
    {
        // The logic for acceptance of your certificates here
        return true;
    }
}

Используется таким образом:

using (new ServicePointManagerX509Helper())
{
    // Your code here
}

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

Другие вопросы по тегам