SSL Mutual Authentication, with one specific Root Certificate

Posted on

Problem

For a new server application we are developing; I implemented a method to verify SSL certificates.
The use of this method is Mutual Authentication: to prove that the server and client are who they say they are, using a Root Certificate and Intermediate Certificate that I’ve created myself.

The SslStream is created as such (server-side):

this.stream = new SslStream(tcpClient.GetStream(), false, SSLVerification.VerifyCertificate, null);
var certificate = new X509Certificate2(Config.Instance.CertificatePath, Config.Instance.CertificatePassword);
stream.AuthenticateAsServer(certificate, true, SslProtocols.Tls, false);

And Client side (almost) the same:

SslStream stream = new SslStream(client.GetStream(), false, SSLVerification.VerifyCertificate, null);
X509Certificate2 certificate = new X509Certificate2(Config.Instance.CertificatePath, Config.Instance.CertificatePassword);
stream.AuthenticateAsClient("our.server.name", new X509Certificate2Collection(certificate), System.Security.Authentication.SslProtocols.Tls, false);

Both snippets make use of the same library, in which the SslVerification.VerifyCertificate() can be found.

The implementation of that method is as follows:

public static bool VerifyCertificate(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
{
  chain.ChainPolicy.VerificationFlags = X509VerificationFlags.AllowUnknownCertificateAuthority;

  var rootCert = new X509Certificate2(X509Certificate.CreateFromCertFile(rootCertificate));
  var interCert = new X509Certificate2(X509Certificate.CreateFromCertFile(intermediateCertificate));

  chain.ChainPolicy.ExtraStore.Add(interCert);
  chain.ChainPolicy.ExtraStore.Add(rootCert);

  var isChainValid = chain.Build(new X509Certificate2(certificate));

  return isChainValid && chain.ChainElements.OfType<X509ChainElement>().Any(c => c.Certificate.Thumbprint == rootCert.Thumbprint);
}

After the SslStream has been set-up, we run a check (server-side) on the common-name to verify exactly which client we are talking to. We just simply look up the Common Name (Certificate.Subject) in a database table.

My questions are:

  1. Is this the correct way to ensure that the certificate has been signed by our specific Root Certificate, and not any other signing certificate?
  2. Is there any way to fool this implementation?
  3. Maybe any other pitfalls in the implementation above?

Solution

The X509 certificate contains by itself a system to specify and verify chain and root certificates, by doing this you are actually creating a certificate without a proper root certificate but implementing a root certificate by yourself.
Looks like a waste of time just for the pleasure of doing something less secure…

Leave a Reply

Your email address will not be published. Required fields are marked *