Как вызвать проверку сертификата по умолчанию при переопределении ServicePointManager.ServerCertificateValidationCallback в С#?
Мне нужно доверять некоторым самозаверяющим сертификатам в приложении, поэтому я переопределяю обратный вызов проверки следующим образом:
ServicePointManager.ServerCertificateValidationCallback = MyRemoteCertificateValidationCallback;
...
public static bool MyRemoteCertificateValidationCallback(
Object sender,
X509Certificate certificate,
X509Chain chain,
SslPolicyErrors sslPolicyErrors)
{
if (sslPolicyErrors == SslPolicyErrors.None)
return true;
if (IsAprrovedByMyApplication(sender, certificate)) // <-- no matter what the check here is
return true;
else
return false; // <-- here I'd like to call the default Windwos handler rather than returning 'false'
}
Но когда есть некоторые ошибки политики, и сайт, с которым я подключаюсь, не одобрен приложением, исключается Exception.
Проблема здесь в том, что она отличается от стандартного поведения Windows.
Рассмотрим этот сайт: https://www.dscoduc.com/
У этого сертификата есть неизвестный эмитент, и поэтому он не доверен. Я добавил его с MMC к доверенным людям Local Copmuter (это Windows 7).
Если я запустил этот код без переопределения обратного вызова проверки сертификата:
HttpWebRequest http = (HttpWebRequest)HttpWebRequest.Create("https://www.dscoduc.com/");
using (WebResponse resp = http.GetResponse())
{
using (StreamReader sr = new StreamReader(resp.GetResponseStream()))
{
string htmlpage = sr.ReadToEnd();
}
}
он успешно соединяется. Это означает, что валидатор по умолчанию Windows решил доверять этому сертификату.
Но как только я переопределяю ServerCertificateValidationCallback, мой обратный вызов вызывается с помощью SslPolicyErrors.RemoteCertificateChainErrors
и цепочка содержит один элемент со статусом X509ChainStatusFlags.PartialChain(на самом деле я бы не ожидал, что здесь не будет ошибок, потому что предполагается, что текущий сертификат должен быть доверенным)
Этот сайт не включен в мой доверенный список и не хочет возвращать 'true' из моего обратного вызова.
Но я не хочу возвращать "ложный" ни один, или я получу исключение: "Удаленный сертификат недействителен в соответствии с процедурой проверки", что явно не ожидается для https://www.dscoduc.com/, потому что он добавлен в хранилище доверенных лиц и одобрен Windows, когда обратный вызов сертификата не переоценивается.
Поэтому я хочу, чтобы Windows приняла процедуру проверки по умолчанию для этого сайта. Я не хочу заглядывать в хранилища Windows Trusted и просматривать все элементы цепочки, потому что он уже (и, надеюсь, правильно) реализован в Windows.
Другими словами, мне нужно явно доверять сайтам, одобренным пользователем (которые где-то хранятся в его настройках), и вызвать проверку сертификации по умолчанию для всех остальных.
Значение по умолчанию для ServicePointManager.ServerCertificateValidationCallback равно null, поэтому для меня не требуется обратного вызова по умолчанию.
Как я могу назвать этот обработчик сертификата по умолчанию?
Спасибо
Ответы
Ответ 1
Это менее сложно, чем вы думаете, чтобы пройти цепочку из своего обратного вызова.
Посмотрите http://msdn.microsoft.com/en-us/library/dd633677(v=exchg.80).aspx
Код в этом примере исследует цепочку сертификатов для разработки, если сертификат самоподписан, и если да, доверяйте ему. Вы можете адаптировать это, чтобы принять PartialChain
вместо этого. Вы бы хотели сделать что-то вроде этого:
if (status.Status == X509ChainStatusFlags.PartialChain ||
(certificate.Subject == certificate.Issuer &&
status.Status == X509ChainStatusFlags.UntrustedRoot)
{
// Certificates with a broken chain and
// self-signed certificates with an untrusted root are valid.
continue;
}
else if (status.Status != X509ChainStatusFlags.NoError)
{
// If there are any other errors in the certificate chain,
// the certificate is invalid, so the method returns false.
return false;
}
В качестве альтернативы проверьте свойство Subject
:
private static bool CertificateValidationCallBack(
object sender,
System.Security.Cryptography.X509Certificates.X509Certificate certificate,
System.Security.Cryptography.X509Certificates.X509Chain chain,
System.Net.Security.SslPolicyErrors sslPolicyErrors)
{
return certificate.Subject.Contains(".dsoduc.com");
}
Ответ 2
Что-то вроде этого может работать. Обратите внимание, что X509CertificateValidator позволяет вам выбрать, включать ли хранилище доверенных лиц в валидацию.
private static bool CertificateValidationCallBack(
object sender,
X509Certificate certificate,
X509Chain chain,
SslPolicyErrors sslPolicyErrors)
{
// Your custom check here...
if (isYourSpecialCase)
{
return true;
}
// If it is not your special case then revert to default checks...
// Convert the certificate to a X509Certificate2
var certificate2 = certificate as X509Certificate2 ?? new X509Certificate2(certificate);
try
{
// Choose the type of certificate validation you want
X509CertificateValidator.PeerOrChainTrust.Validate(certificate2);
//X509CertificateValidator.ChainTrust.Validate(certificate2);
}
catch
{
return false;
}
// Sender is always either a WebReqest or a hostname string
var request = sender as WebRequest;
string requestHostname = request != null ? request.RequestUri.Host : (string)sender;
// Get the hostname from the certificate
string certHostname = certificate2.GetNameInfo(X509NameType.DnsName, false);
return requestHostname.Equals(certHostname, StringComparison.InvariantCultureIgnoreCase);
}