Как вызвать проверку сертификата по умолчанию при переопределении ServicePointManager.ServerCertificateValidationCallback в C#?
мне нужно доверять некоторым самозаверяющим сертификатам в приложении, поэтому я переопределяю обратный вызов проверки следующим образом:
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'
}
но когда есть некоторые ошибки политики, и сайт, к которому я подключаюсь, не одобрен приложением, возникает исключение. Проблема здесь заключается в том, что он отличается от стандартного поведения Windows.
рассмотрим этот сайт:https://www.dscoduc.com/
это сертификат имеет неизвестного эмитента, и поэтому недоверяемый. Я добавил его с MMC к доверенным людям локального Копмьютера (это 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" из моего обратного вызова. Но я также не хочу возвращать "false", или я получу исключение: "удаленный сертификат недействителен в соответствии с процедурой проверки", что, очевидно, не ожидается для https://www.dscoduc.com/, потому что он добавлен в хранилище доверенных людей и одобрен Windows, когда обратный вызов сертификата не переопределен. Поэтому я хочу, чтобы продолжить процедуру проверки по умолчанию для этого сайта. Я не хочу сам заглядывать в доверенные магазины Windows и просматривать все элементы цепочки, потому что он уже (и, надеюсь, правильно) реализован в Windows.
иными словами, Мне нужно явно доверия к сайтам approvied пользователем (которые хранятся где-то в его настройках), и проверка аттестация по умолчанию для всех остальных.
значение по умолчанию для ServicePointManager.ServerCertificateValidationCallback имеет значение null, поэтому для меня нет обратного вызова по умолчанию. Как мне назвать этот обработчик сертификатов "по умолчанию"?
спасибо
3 ответов
что-то вроде этого может сработать. Примечание. 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);
}
это менее сложно, чем вы думаете, чтобы пройти цепь из вашего обратного вызова.
посмотрите на 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;
}
кроме того, проверить the 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");
}
@pete.решение c, похоже, работает правильно (проверено в разных случаях)
однако, если все еще не уверен, что X509CertificateValidator проверяет то же самое, обратный вызов по умолчанию может быть запущен через отражение:
private static object s_defaultCallback;
private static MethodInfo s_defaultCallbackInvoker;
...
// Get the original callback using reflection
PropertyInfo[] pis = typeof (ServicePointManager).GetProperties(BindingFlags.Static | BindingFlags.NonPublic);
foreach (var pi in pis)
{
if (pi.Name == "CertPolicyValidationCallback")
{
s_defaultCallback = pi.GetValue(null, null);
s_defaultCallbackInvoker = s_defaultCallback.GetType().GetMethod("Invoke", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
break;
}
}
...
private static bool CertificateValidationCallBack(
object sender,
X509Certificate certificate,
X509Chain chain,
SslPolicyErrors sslPolicyErrors)
{
// Your custom check here...
if (isYourSpecialCase)
{
return true;
}
// Default Windows behavior
WebRequest req = sender as WebRequest;
if (req == null)
return false;
ServicePoint sp = ServicePointManager.FindServicePoint(req.RequestUri);
string host = req.RequestUri.Host;
object [] parameters = new object[]
{
host,
sp,
certificate,
req,
chain,
sslPolicyErrors
};
return (bool)s_defaultCallbackInvoker.Invoke(s_defaultCallback, parameters);
}