THRIFT-3050 Client certificate authentication for non-http TLS in C#
Client: C#
Patch: Hans-Peter Klett <hansk@spectralogic.com>

This closes #410

This plumbs the remote certificate validation callback down to the server side authentication code. When the callback is set, we set the client certificate required flag. In practice, the validation callback still gets called even when the client doesn't supply a certificate.

I've taken the liberty of using slightly more modern C# syntax to shorten up the affected method.
diff --git a/lib/csharp/src/Transport/TTLSServerSocket.cs b/lib/csharp/src/Transport/TTLSServerSocket.cs
index 9753d47..2e2d299 100644
--- a/lib/csharp/src/Transport/TTLSServerSocket.cs
+++ b/lib/csharp/src/Transport/TTLSServerSocket.cs
@@ -18,6 +18,7 @@
  */
 
 using System;
+using System.Net.Security;
 using System.Net.Sockets;
 using System.Security.Cryptography.X509Certificates;
 
@@ -53,6 +54,10 @@
         /// </summary>
         private X509Certificate serverCertificate;
 
+        /// <summary>
+        /// The function to validate the client certificate.
+        /// </summary>
+        private RemoteCertificateValidationCallback clientCertValidator;
 
         /// <summary>
         /// Initializes a new instance of the <see cref="TTLSServerSocket" /> class.
@@ -82,7 +87,8 @@
         /// <param name="clientTimeout">Send/receive timeout.</param>
         /// <param name="useBufferedSockets">If set to <c>true</c> [use buffered sockets].</param>
         /// <param name="certificate">The certificate object.</param>
-        public TTLSServerSocket(int port, int clientTimeout, bool useBufferedSockets, X509Certificate2 certificate)
+        /// <param name="clientCertValidator">The certificate validator.</param>
+        public TTLSServerSocket(int port, int clientTimeout, bool useBufferedSockets, X509Certificate2 certificate, RemoteCertificateValidationCallback clientCertValidator = null)
         {
             if (!certificate.HasPrivateKey)
             {
@@ -92,6 +98,7 @@
             this.port = port;
             this.serverCertificate = certificate;
             this.useBufferedSockets = useBufferedSockets;
+            this.clientCertValidator = clientCertValidator;
             try
             {
                 // Create server socket
@@ -143,7 +150,7 @@
                 client.SendTimeout = client.ReceiveTimeout = this.clientTimeout;
 
                 //wrap the client in an SSL Socket passing in the SSL cert
-                TTLSSocket socket = new TTLSSocket(client, this.serverCertificate, true);
+                TTLSSocket socket = new TTLSSocket(client, this.serverCertificate, true, this.clientCertValidator);
 
                 socket.setupTLS();
 
diff --git a/lib/csharp/src/Transport/TTLSSocket.cs b/lib/csharp/src/Transport/TTLSSocket.cs
index 9e1f9f2..ca8ee41 100644
--- a/lib/csharp/src/Transport/TTLSSocket.cs
+++ b/lib/csharp/src/Transport/TTLSSocket.cs
@@ -77,10 +77,12 @@
         /// <param name="client">An already created TCP-client</param>
         /// <param name="certificate">The certificate.</param>
         /// <param name="isServer">if set to <c>true</c> [is server].</param>
-        public TTLSSocket(TcpClient client, X509Certificate certificate, bool isServer = false)
+        /// <param name="certValidator">User defined cert validator.</param>
+        public TTLSSocket(TcpClient client, X509Certificate certificate, bool isServer = false, RemoteCertificateValidationCallback certValidator = null)
         {
             this.client = client;
             this.certificate = certificate;
+            this.certValidator = certValidator;
             this.isServer = isServer;
 
             if (IsOpen)
@@ -251,27 +253,16 @@
         /// </summary>
         public void setupTLS()
         {
+            this.secureStream = new SslStream(this.client.GetStream(), false, this.certValidator ?? CertificateValidator);
             if (isServer)
             {
                 // Server authentication
-                this.secureStream = new SslStream(this.client.GetStream(), false);
-                this.secureStream.AuthenticateAsServer(this.certificate, false, SslProtocols.Tls, true);
+                this.secureStream.AuthenticateAsServer(this.certificate, this.certValidator != null, SslProtocols.Tls, true);
             }
             else
             {
                 // Client authentication
-                X509CertificateCollection validCerts = new X509CertificateCollection();
-                validCerts.Add(certificate);
-
-                if (this.certValidator != null)
-                {
-                    this.secureStream = new SslStream(this.client.GetStream(), false, new RemoteCertificateValidationCallback(this.certValidator));
-                }
-                else
-                {
-                    this.secureStream = new SslStream(this.client.GetStream(), false, new RemoteCertificateValidationCallback(CertificateValidator));
-                }
-                this.secureStream.AuthenticateAsClient(host, validCerts, SslProtocols.Tls, true);
+                this.secureStream.AuthenticateAsClient(host, new X509CertificateCollection { certificate }, SslProtocols.Tls, true);
             }
 
             inputStream = this.secureStream;