C#, HttpWebRequest: использование самоподписанного (self-signed) сертификата.
Если попытаться соединиться с HTTPS-сервером, имеющим самоподписанный сертификат, то
HttpWebRequest сгенерирует исключение WebException Базовое соединение закрыто: Не удалось установить доверительные отношения для защищенного канала SSL/TLS. (The underlying connection was closed: Could not establish trust relationship for the SSL/TLS secure channel.). Оно и понятно, HttpWebRequest не смог проверить сертификат узла и закономерно нас послал.Решение заключается в переопределении глобального обработчика
System.Net.ServicePointManager.ServerCer tificateValidationCallback.Внимание! Изменение
ServerCertificateValidationCallback повлияет на все соединения в программе, пока обработчик не будет установлен по умолчанию, надо не забывать это, и возвращать обработчик к стандартному значению, после того, как работа с сервером, имеющим самоподписанный сертификат, завершена.Предположим, есть класс-обертка над
HttpWebRequest, надо его дополнить так, чтоб можно было работать с серверами с self-signed сертификатами.Самый простой способ, но не самый безопасный.
ServerCertificateValidationCallback передается делегат, который всегда возвращает true. Добавляем в класс функцию:public void EnableIgnoreCertError()
{
ServicePointManager.ServerCertificateValidationCallback =
delegate { return true; };
}И функцию, которая возвращает
ServerCertificateValidationCallback к значению по умолчанию:public void DisableIgnoreCertError()
{
ServicePointManager.ServerCertificateValidationCallback =
null;
}Начали работать с сервером - вызвали первую, закончили - вызвали вторую.
Немного более сложный, но более безопасный способ. Необходимо заранее либо иметь сам сертификат, либо знать его хэш. В следующем примере будем проверять как раз хэш SHA1 сертификата.
1. Подключим в References'ах System.Security.Cryptography.
2. Подключим необходимые пространства имен:
using System.Net.Security;
using System.Security.Cryptography.X509Certifi cates;3. Заведем в классе поле, куда из вызывающей программы будем передавать заранее известный хэш сертификата:
public string CertHashString { get; set; }4. Заведем две функции, для включения и выключения самостоятельной проверки сертификата:
public void EnableValidateCert()
{
ServicePointManager.ServerCertificateValidationCallback =
ValidateCert;
}
public void DisableValidateCert()
{
ServicePointManager.ServerCertificateValidationCallback =
null;
}В первой функции вместо делегата, возвращающего
true, указываем нашу функцию проверки.Функция проверки сертификата обязательно должна иметь следующий заголовок:
bool FunctionName (object, X509Certificate, X509Chain, SslPolicyErrors), т.е., например:private bool ValidateCert(object sender, X509Certificate cert,
X509Chain chain, SslPolicyErrors sslPolicyErrors)В функции:
1. Проверяем наличие ошибок SSL, если их нет, значит сертификат уже был опознан:
if (sslPolicyErrors == SslPolicyErrors.None)
{
return true;
}2. Если известный хэш сертификата не был передан вызывающей программой, значит и проверять нечего:
if (string.IsNullOrEmpty(CertHashString))
{
return false;
}3. Получаем хэш сертификата сервера в виде строки:
string hashstring = cert.GetCertHashString();4. Если хэши полученного и известного сертификата совпадают - все ок, возвращаем
true, иначе false.if (hashstring == CertHashString)
{
return true;
}
return false;Функция целиком:
private bool ValidateCert(object sender, X509Certificate cert,
X509Chain chain, SslPolicyErrors sslPolicyErrors)
{
//все и так хорошо
if (sslPolicyErrors == SslPolicyErrors.None)
{
return true;
}
//не передан хэш сертификата
//значит и проверять нечего
if (string.IsNullOrEmpty(CertHashString))
{
return false;
}
//получаем хэш сертификата сервера в виде строки
string hashstring = cert.GetCertHashString();
//если хэши полученного и известного
//сертификата совпадают - все ок.
if (hashstring == CertHashString)
{
return true;
}
return false;
}Класс целиком на PasteBin
На GitHub
Источник
Это репост с сайта http://tolik-punkoff.com
Оригинал: http://tolik-punkoff.com/2020/01/09/c-ht