THRIFT-2347 C# TLS Transport based on THRIFT-181
Client: C#
Patch: Beat Käslin
This closes #104
commit 21c33abd59a2333c48722933c6894d8ed145e638
Author: Beat Kaeslin <beat.kaeslin@siemens.com>
Date: 2014-04-16T14:07:58Z
Add TLS transport for C#
commit 60a0baa1797b0ef0ea6f8c21e5b81a78cdfcdf16
Author: Beat Kaeslin <beat.kaeslin@siemens.com>
Date: 2014-04-17T06:23:57Z
csharp tests moved to the end
diff --git a/lib/csharp/src/Transport/TTLSSocket.cs b/lib/csharp/src/Transport/TTLSSocket.cs
new file mode 100644
index 0000000..beb5876
--- /dev/null
+++ b/lib/csharp/src/Transport/TTLSSocket.cs
@@ -0,0 +1,284 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+using System;
+using System.Net.Security;
+using System.Net.Sockets;
+using System.Security.Authentication;
+using System.Security.Cryptography.X509Certificates;
+
+namespace Thrift.Transport
+{
+ /// <summary>
+ /// SSL Socket Wrapper class
+ /// </summary>
+ public class TTLSSocket : TStreamTransport
+ {
+ /// <summary>
+ /// Internal TCP Client
+ /// </summary>
+ private TcpClient client = null;
+
+ /// <summary>
+ /// The host
+ /// </summary>
+ private string host = null;
+
+ /// <summary>
+ /// The port
+ /// </summary>
+ private int port = 0;
+
+ /// <summary>
+ /// The timeout for the connection
+ /// </summary>
+ private int timeout = 0;
+
+ /// <summary>
+ /// Internal SSL Stream for IO
+ /// </summary>
+ private SslStream secureStream = null;
+
+ /// <summary>
+ /// Defines wheter or not this socket is a server socket<br/>
+ /// This is used for the TLS-authentication
+ /// </summary>
+ private bool isServer = false;
+
+ /// <summary>
+ /// The certificate
+ /// </summary>
+ private X509Certificate certificate = null;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="TTLSSocket"/> class.
+ /// </summary>
+ /// <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)
+ {
+ this.client = client;
+ this.certificate = certificate;
+ this.isServer = isServer;
+
+ if (IsOpen)
+ {
+ base.inputStream = client.GetStream();
+ base.outputStream = client.GetStream();
+ }
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="TTLSSocket"/> class.
+ /// </summary>
+ /// <param name="host">The host, where the socket should connect to.</param>
+ /// <param name="port">The port.</param>
+ /// <param name="certificatePath">The certificate path.</param>
+ public TTLSSocket(string host, int port, string certificatePath)
+ : this(host, port, 0, X509Certificate.CreateFromCertFile(certificatePath))
+ {
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="TTLSSocket"/> class.
+ /// </summary>
+ /// <param name="host">The host, where the socket should connect to.</param>
+ /// <param name="port">The port.</param>
+ /// <param name="certificate">The certificate.</param>
+ public TTLSSocket(string host, int port, X509Certificate certificate)
+ : this(host, port, 0, certificate)
+ {
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="TTLSSocket"/> class.
+ /// </summary>
+ /// <param name="host">The host, where the socket should connect to.</param>
+ /// <param name="port">The port.</param>
+ /// <param name="timeout">The timeout.</param>
+ /// <param name="certificate">The certificate.</param>
+ public TTLSSocket(string host, int port, int timeout, X509Certificate certificate)
+ {
+ this.host = host;
+ this.port = port;
+ this.timeout = timeout;
+ this.certificate = certificate;
+
+ InitSocket();
+ }
+
+ /// <summary>
+ /// Creates the TcpClient and sets the timeouts
+ /// </summary>
+ private void InitSocket()
+ {
+ this.client = new TcpClient();
+ client.ReceiveTimeout = client.SendTimeout = timeout;
+ client.Client.NoDelay = true;
+ }
+
+ /// <summary>
+ /// Sets Send / Recv Timeout for IO
+ /// </summary>
+ public int Timeout
+ {
+ set
+ {
+ this.client.ReceiveTimeout = this.client.SendTimeout = this.timeout = value;
+ }
+ }
+
+ /// <summary>
+ /// Gets the TCP client.
+ /// </summary>
+ public TcpClient TcpClient
+ {
+ get
+ {
+ return client;
+ }
+ }
+
+ /// <summary>
+ /// Gets the host.
+ /// </summary>
+ public string Host
+ {
+ get
+ {
+ return host;
+ }
+ }
+
+ /// <summary>
+ /// Gets the port.
+ /// </summary>
+ public int Port
+ {
+ get
+ {
+ return port;
+ }
+ }
+
+ /// <summary>
+ /// Gets a value indicating whether TCP Client is Cpen
+ /// </summary>
+ public override bool IsOpen
+ {
+ get
+ {
+ if (this.client == null)
+ {
+ return false;
+ }
+
+ return this.client.Connected;
+ }
+ }
+
+ /// <summary>
+ /// Validates the certificates!<br/>
+ /// </summary>
+ /// <param name="sender">The sender-object.</param>
+ /// <param name="certificate">The used certificate.</param>
+ /// <param name="chain">The certificate chain.</param>
+ /// <param name="sslPolicyErrors">An enum, which lists all the errors from the .NET certificate check.</param>
+ /// <returns></returns>
+ private bool CertificateValidator(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslValidationErrors)
+ {
+ return (sslValidationErrors == SslPolicyErrors.None);
+ }
+
+ /// <summary>
+ /// Connects to the host and starts the routine, which sets up the TLS
+ /// </summary>
+ public override void Open()
+ {
+ if (IsOpen)
+ {
+ throw new TTransportException(TTransportException.ExceptionType.AlreadyOpen, "Socket already connected");
+ }
+
+ if (String.IsNullOrEmpty(host))
+ {
+ throw new TTransportException(TTransportException.ExceptionType.NotOpen, "Cannot open null host");
+ }
+
+ if (port <= 0)
+ {
+ throw new TTransportException(TTransportException.ExceptionType.NotOpen, "Cannot open without port");
+ }
+
+ if (client == null)
+ {
+ InitSocket();
+ }
+
+ client.Connect(host, port);
+
+ setupTLS();
+ }
+
+ /// <summary>
+ /// Creates a TLS-stream and lays it over the existing socket
+ /// </summary>
+ public void setupTLS()
+ {
+ if (isServer)
+ {
+ // Server authentication
+ this.secureStream = new SslStream(this.client.GetStream(), false);
+ this.secureStream.AuthenticateAsServer(this.certificate, false, SslProtocols.Tls, true);
+ }
+ else
+ {
+ // Client authentication
+ X509CertificateCollection validCerts = new X509CertificateCollection();
+ validCerts.Add(certificate);
+
+ this.secureStream = new SslStream(this.client.GetStream(), false, new RemoteCertificateValidationCallback(CertificateValidator));
+ this.secureStream.AuthenticateAsClient(host, validCerts, SslProtocols.Tls, true);
+ }
+
+ inputStream = this.secureStream;
+ outputStream = this.secureStream;
+ }
+
+ /// <summary>
+ /// Closes the SSL Socket
+ /// </summary>
+ public override void Close()
+ {
+ base.Close();
+ if (this.client != null)
+ {
+ this.client.Close();
+ this.client = null;
+ }
+
+ if (this.secureStream != null)
+ {
+ this.secureStream.Close();
+ this.secureStream = null;
+ }
+ }
+ }
+}