Client-authenticated TLS in C#

Thanks to NSA, most probably every developer is aware of the HTTPS and the underlying TLS (or older SSL). While most scenarios involve authentication of a server, authentication of a client is often overlooked.

If you wonder what you gain, just be reminded of key-based authentication in the SSH. No need to exchange username/password with every client. You just exchange a (safely stored) key and you know who is on the other side.

Distribution and a safe storage of the client certificate is a non-trivial problem but easily handable on a smaller scale. Windows certificate store is not too bad and the client authentication makes it easy to block keys that aren't trusted any more.

Here is the example code of a simple TLS encrypted TCP client/server with a self-signed certificates. Of course, one would expect proper certificates to be used in any production environment, but these will do in a pinch.

First we need to setup a server using just a standard TCP listener with a twist:

var serverCertificate = new X509Certificate2(ServerCertificateFile);

var listener = new TcpListener(IPAddress.Any, ServerPort);
listener.Start();

while (true) {
using (var client = listener.AcceptTcpClient())
using (var sslStream = new SslStream(client.GetStream(), false, App_CertificateValidation)) {
sslStream.AuthenticateAsServer(serverCertificate, true, SslProtocols.Tls12, false);

//send/receive from the sslStream
}
}

Client is equally simple:

var clientCertificate = new X509Certificate2(ClientCertificateFile);
var clientCertificateCollection = new X509CertificateCollection(new X509Certificate[] { clientCertificate });

using (var client = new TcpClient(ServerHostName, ServerPort))
using (var sslStream = new SslStream(client.GetStream(), false, App_CertificateValidation)) {
sslStream.AuthenticateAsClient(ServerCertificateName, clientCertificateCollection, SslProtocols.Tls12, false);

//send/receive from the sslStream
}

Only trick in validation is to allow certificate chain errors. That is needed for self-signed certificates to work:

bool App_CertificateValidation(Object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors) {
if (sslPolicyErrors == SslPolicyErrors.None) { return true; }
if (sslPolicyErrors == SslPolicyErrors.RemoteCertificateChainErrors) { return true; } //we don't have a proper certificate tree
return false;
}

It is really this simple to convert any TCP socket code into the encrypted TLS.

Full example is available for download.

8 thoughts to “Client-authenticated TLS in C#”

  1. Cool, worked first time I built and ran it. Unlike 99 percent of the C#/SSL samples out there!

    Only thing is: How do you make the X509 certificate file, which appears to be binary? I’ll google it :-)

  2. Hey,

    Any idea why this would work with your certificates but If I make my own it errors when reading data for inputBytes?

    {“An existing connection was forcibly closed by the remote host”}

    1. I also get:

      The remote certificate is invalid according to the validation procedure.

      On the client side. Don’t know why though

  3. Hi.
    This is a nice post. I workin a same solution with TLS 1.3 but unfortunently, only work without a client authentication.
    Are you planning to create a similar post with tls13?

Leave a Reply to grais Cancel reply

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