How to add a trusted CA certificate (NOT a client

2019-02-09 19:38发布

问题:

I wrote a C# program that uses HttpWebRequest to connect to an HTTPS site. The GetResponse() method throws an exception:

SystemError: The underlying connection was closed: Could not establish trust relationship for the SSL/TLS secure channel.

I'm able to connect to the same website using curl.exe --cacert CAFile.pem. I'd like to be able use the same trusted CA certificates from the C# program.

How can I get HttpWebRequest to use this CA certificate file (or an X509CertificateCollection containing certificates parsed from it)?

回答1:

Try setting your ServerCertificateValidationCallback to use this:

public static bool ValidateServerCertificate(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
{
    if (sslPolicyErrors == SslPolicyErrors.None)
    return true;

    X509Chain privateChain = new X509Chain();
    privateChain.ChainPolicy.RevocationMode = X509RevocationMode.Offline;

    X509Certificate2 cert2 = new X509Certificate2(certificate);
    X509Certificate2 signerCert2 = new X509Certificate2(@"C:\MyCACert.PEM");

    privateChain.ChainPolicy.ExtraStore.Add(signerCert2);       
    privateChain.Build(cert2);

    bool isValid = true;

    foreach (X509ChainStatus chainStatus in privateChain.ChainStatus)
    {
        if (chainStatus.Status != X509ChainStatusFlags.NoError)
        {
            isValid = false;
            break;
        }
    }

    return isValid;
}

I have not had an opportunity to test this, so let me know if you encounter any errors and I'll modify the answer if needed.



回答2:

The solution I ultimately implemented was to write a class implementing ICertificatePolicy with custom validation logic:

private X509CertificateCollection   _certs;
private ICertificatePolicy          _defaultPolicy;

public bool CheckValidationResult(ServicePoint svcPoint, X509Certificate cert, WebRequest req, int problem)
{
    if ((_defaultPolicy != null) && _defaultPolicy.CheckValidationResult(svcPoint, cert, req, problem))
    {
        return true;
    }

    foreach (X509Certificate caCert in _certs)
    {
        if (caCert.Equals(cert))
        {
            return true;
        }
    }

    return false;
}

(Error-checking omitted for brevity.)

_defaultPolicy can be set to ServicePointManager.CertificatePolicy to allow the default certificate store to be used in addition to custom certificates.

_certs contains the extra certificate(s). It's generated by parsing the PEM file and calling _certs.Add(new X509Certificate(Convert.FromBase64String(base64cert)));

CertificatePolicy has been obsoleted by ServerCertificateValidationCallback, but I needed to support an old version of .NET.