How to call the default certificate check when overriding ServicePointManager.ServerCertificateValidationCallback in C#?

和自甴很熟 提交于 2019-11-30 11:23:41

Something like this might work. Note the X509CertificateValidator allows you to choose whether to include the Trusted People store in the validation.

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);
}

It's less difficult than you think to walk the chain from within your callback.

Have a look at http://msdn.microsoft.com/en-us/library/dd633677(v=exchg.80).aspx

The code in that sample examines the certificate chain to work out if the certificate is self-signed and if so, trust it. You could adapt that to accept a PartialChain instead or as well. You'd be looking to do something like this:

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;
}

Alternatively, inspect the Subject property:

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");
}

The @pete.c's solution seems to work correctly (checked different cases)

However, if still unsure that X509CertificateValidator validates the same way, the default callback can be run through reflection:

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);
}
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!