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)?
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.
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.