11
\$\begingroup\$

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?
\$\endgroup\$
  • \$\begingroup\$ "Is there any way to fool this implementation?" How far do you want to go with that? The client, unless running on secure hardware, can be tampered then honestly IMO you do not add that much security for a motivated attacker. Also, working with self-signed certificates you may have some issues if you want to invalidate them (I think). \$\endgroup\$ – Adriano Repetti Aug 25 '17 at 11:38
1
\$\begingroup\$

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

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.