My C#.NET SSL connect works when I import the certificate manually in IE (Tools/Internet Options/Content/Certificates), but how can I load the certificate by code? Here is m
A quick Google pointed me to a piece of text from the Microsoft SslStream class.
The authentication is handled by the Security Support Provider (SSPI) channel provider. The client is given an opportunity to control validation of the server's certificate by specifying a RemoteCertificateValidationCallback delegate when creating an SslStream. The server can also control validation by supplying a RemoteCertificateValidationCallback delegate. The method referenced by the delegate includes the remote party's certificate and any errors SSPI encountered while validating the certificate. Note that if the server specifies a delegate, the delegate's method is invoked regardless of whether the server requested client authentication. If the server did not request client authentication, the server's delegate method receives a null certificate and an empty array of certificate errors.
So simply implement the delegate and do the verification yourself.
I wrote another method to add my certificate to Trusted Root Certification Authorities (root) before attempting to authenticate as client via SSLStream object:
public static void InstallCertificate()
{
X509Store store = new X509Store(StoreName.Root, StoreLocation.LocalMachine);
store.Open(OpenFlags.ReadWrite);
string fileName = "sslcert.pem";
X509Certificate2 certificate1;
try
{
certificate1 = new X509Certificate2(fileName);
}
catch (Exception ex)
{
throw new Exception("Error loading SSL certificate file." + Environment.NewLine + fileName);
}
store.Add(certificate1);
store.Close();
}
And then:
InstallCertificate();
sslStream.AuthenticateAsClient("Test");
It works fine without any warnings or errors. But base question still remains unsolved:
How can I use a certificate to authenticate as client without installing it in Windows?
To build upon owlstead's answer, here's how I use a single CA certificate and a custom chain in the verification callback to avoid Microsoft's store.
I have not figured out how to use this chain (chain2
below) by default such that there's no need for the callback. That is, install it on the ssl socket and the connection will "just work". And I have not figured out how install it such that its passed into the callback. That is, I have to build the chain for each invocation of the callback. I think these are architectural defects in .Net, but I might be missing something obvious.
The name of the function does not matter. Below, VerifyServerCertificate
is the same callback as RemoteCertificateValidationCallback
. You can also use it for the ServerCertificateValidationCallback
in ServicePointManager
.
static bool VerifyServerCertificate(object sender, X509Certificate certificate,
X509Chain chain, SslPolicyErrors sslPolicyErrors)
{
try
{
String CA_FILE = "ca-cert.der";
X509Certificate2 ca = new X509Certificate2(CA_FILE);
X509Chain chain2 = new X509Chain();
chain2.ChainPolicy.ExtraStore.Add(ca);
// Check all properties
chain2.ChainPolicy.VerificationFlags = X509VerificationFlags.NoFlag;
// This setup does not have revocation information
chain2.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck;
// Build the chain
chain2.Build(new X509Certificate2(certificate));
// Are there any failures from building the chain?
if (chain2.ChainStatus.Length == 0)
return true;
// If there is a status, verify the status is NoError
bool result = chain2.ChainStatus[0].Status == X509ChainStatusFlags.NoError;
Debug.Assert(result == true);
return result;
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
return false;
}