Лучшие практики для использования 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;
}
};
Обратите внимание, что это все равно повлияет на другие экземпляры веб-клиента в том же домене приложения (они все примут указанную строку хеша), но по крайней мере это не будет блокировать другие сертификаты.
Я знаю, что немного опоздал на вечеринку, но другой вариант - использовать класс, наследующий 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, вам нужно установить полную версию (как с открытым, так и с закрытым ключом) в доверенном корне, чтобы это работало.