如何在C#中覆盖ServicePointManager.ServerCertificateValidationCallback时调用默认证书检查?

mis*_*ika 15 .net ssl certificate x509certificate

我需要相信应用程序中的一些自签名证书,所以我覆盖了这样的验证回调:

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'
}
Run Code Online (Sandbox Code Playgroud)

但是当出现一些策略错误,并且我连接的站点未被应用程序批准时,将抛出异常.这里的问题是它与标准的Windows行为不同.

考虑一下这个网站:https://www.dscoduc.com/

它的证书有一个未知的发行者,因此不受信任.我已将它与MMC一起添加到Local Copmuter的Trusted People(它是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();
    }
}
Run Code Online (Sandbox Code Playgroud)

它成功连接.这意味着Windows默认验证程序决定信任此证书.

但是,一旦我重写ServerCertificateValidationCallback,我的回调函数调用SslPolicyErrors.RemoteCertificateChainErrors 和链中包含与状态一个元素X509ChainStatusFlags.PartialChain(其实我希望在这里收到任何错误,监守当前证书被认为是可信的)

此网站未包含在我的受信任列表中,并且不希望从我的回调中返回"true".但我不想也不会返回'false',否则我会得到一个异常:"远程证书根据验证程序无效",这显然不是https://www.dscoduc.com/的预期,因为它已添加到"受信任的人"商店,并且在未覆盖证书回调时被Windows批准.所以我希望Windows采用此站点的默认验证程序.我不想自己查看Windows Trusted存储并浏览所有链元素,因为它已经(并且希望在Windows中)正确实现.

换句话说,我需要明确信任用户认可的网站(存储在他的设置中的某个地方),并为所有其他网站调用默认的认证检查.

ServicePointManager.ServerCertificateValidationCallback的默认值为null,因此以后没有"默认"回调供我调用.我该怎么称呼这个'默认'证书处理程序?

谢谢

Chr*_*ams 6

从你的回调中走出链条比你想象的要困难.

看看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;
}
Run Code Online (Sandbox Code Playgroud)

或者,检查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");
}
Run Code Online (Sandbox Code Playgroud)


pet*_*e.c 5

像这样的事情可能会奏效。请注意,X509CertificateValidator 允许您选择是否在验证中包含 Trusted People 存储。

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);
}
Run Code Online (Sandbox Code Playgroud)