I have custom server/client applications that communicate to each other using an SSL-encrypted TCP connection (custom protocol). To setup the server certificate, I have created a self-signed certificate authority and used that to sign a certificate for the server to use. On the client side, I would like to verify that the certificate of the server I am connecting to was signed by my self-signed CA.
I was able to get this working in C++ with boost by giving the ssl::context the self-signed CA certificate using the load_verify_file() function. I would like to implement the same functionality in a C# client, but the .NET SSL stuff seems to be much more stringent about trusting certificates that are not in the Windows trust store.
I have found several partial solutions in my searching around so far. Apparently X509Chain is the class to use to verify SSL certificates. I have tried code like this but the manually created chain still complains that the root certificate is untrusted, just like the original chain that gets passed into the verification function. There is an option to ignore unknown certificate authorities, but that seems to mean that it will accept any self-signed certificate, which is definitely not what I want.
This is the code that seems to come closest to what I want, but as stated above, I have the problem that it still complains about the certificate I add to the ExtraStore being self-signed. Is there any way to convince X509Chain to trust the certificate I am giving it?
bool remoteCertificateValidationCallback(
object sender, X509Certificate certificate, X509Chain chain,
SslPolicyErrors sslPolicyErrors)
{
// make sure certificate was signed by our CA cert
X509Chain verify = new X509Chain();
verify.ChainPolicy.ExtraStore.Add(secureClient.CertificateAuthority); // add CA cert for verification
//verify.ChainPolicy.VerificationFlags = X509VerificationFlags.AllowUnknownCertificateAuthority; // this accepts too many certificates
verify.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck; // no revocation checking
verify.ChainPolicy.RevocationFlag = X509RevocationFlag.ExcludeRoot;
if (verify.Build(new X509Certificate2(certificate)))
{
return true; // success?
}
return false;
}
I suspect that your private CA certificate is not installed on the current system (where you run the code) or it is installed incorrectly. The root CA certificate MUST be installed in the Trusted Root CAs container of Computer stire, not in the current user store. By default,
X509Chain
uses computer store to lookup for trusted anchors.Also, your code do not perform what you want. It will accept and pass for any publically trusted root CA. Instead, you need to compare the last element in the
X509Chain.ChainElements
, whether the contained certificate is the one you are expecting (by comparing thumbprint values). The foolowing fix should apply:where
cacert
is your root CA certificate.