/** * 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 { /// /// SSL Socket Wrapper class /// public class TTLSSocket : TStreamTransport { /// /// Internal TCP Client /// private TcpClient client; /// /// The host /// private string host; /// /// The port /// private int port; /// /// The timeout for the connection /// private int timeout; /// /// Internal SSL Stream for IO /// private SslStream secureStream; /// /// Defines wheter or not this socket is a server socket
/// This is used for the TLS-authentication ///
private bool isServer; /// /// The certificate /// private X509Certificate certificate; /// /// User defined certificate validator. /// private RemoteCertificateValidationCallback certValidator; /// /// The function to determine which certificate to use. /// private LocalCertificateSelectionCallback localCertificateSelectionCallback; /// /// The SslProtocols value that represents the protocol used for authentication.SSL protocols to be used. /// private readonly SslProtocols sslProtocols; /// /// Initializes a new instance of the class. /// /// An already created TCP-client /// The certificate. /// if set to true [is server]. /// User defined cert validator. /// The callback to select which certificate to use. /// The SslProtocols value that represents the protocol used for authentication. public TTLSSocket( TcpClient client, X509Certificate certificate, bool isServer = false, RemoteCertificateValidationCallback certValidator = null, LocalCertificateSelectionCallback localCertificateSelectionCallback = null, // TODO: Enable Tls11 and Tls12 (TLS 1.1 and 1.2) by default once we start using .NET 4.5+. SslProtocols sslProtocols = SslProtocols.Tls) { this.client = client; this.certificate = certificate; this.certValidator = certValidator; this.localCertificateSelectionCallback = localCertificateSelectionCallback; this.sslProtocols = sslProtocols; this.isServer = isServer; if (isServer && certificate == null) { throw new ArgumentException("TTLSSocket needs certificate to be used for server", "certificate"); } if (IsOpen) { base.inputStream = client.GetStream(); base.outputStream = client.GetStream(); } } /// /// Initializes a new instance of the class. /// /// The host, where the socket should connect to. /// The port. /// The certificate path. /// User defined cert validator. /// The callback to select which certificate to use. /// The SslProtocols value that represents the protocol used for authentication. public TTLSSocket( string host, int port, string certificatePath, RemoteCertificateValidationCallback certValidator = null, LocalCertificateSelectionCallback localCertificateSelectionCallback = null, SslProtocols sslProtocols = SslProtocols.Tls) : this(host, port, 0, X509Certificate.CreateFromCertFile(certificatePath), certValidator, localCertificateSelectionCallback, sslProtocols) { } /// /// Initializes a new instance of the class. /// /// The host, where the socket should connect to. /// The port. /// The certificate. /// User defined cert validator. /// The callback to select which certificate to use. /// The SslProtocols value that represents the protocol used for authentication. public TTLSSocket( string host, int port, X509Certificate certificate = null, RemoteCertificateValidationCallback certValidator = null, LocalCertificateSelectionCallback localCertificateSelectionCallback = null, SslProtocols sslProtocols = SslProtocols.Tls) : this(host, port, 0, certificate, certValidator, localCertificateSelectionCallback, sslProtocols) { } /// /// Initializes a new instance of the class. /// /// The host, where the socket should connect to. /// The port. /// The timeout. /// The certificate. /// User defined cert validator. /// The callback to select which certificate to use. /// The SslProtocols value that represents the protocol used for authentication. public TTLSSocket( string host, int port, int timeout, X509Certificate certificate, RemoteCertificateValidationCallback certValidator = null, LocalCertificateSelectionCallback localCertificateSelectionCallback = null, SslProtocols sslProtocols = SslProtocols.Tls) { this.host = host; this.port = port; this.timeout = timeout; this.certificate = certificate; this.certValidator = certValidator; this.localCertificateSelectionCallback = localCertificateSelectionCallback; this.sslProtocols = sslProtocols; InitSocket(); } /// /// Creates the TcpClient and sets the timeouts /// private void InitSocket() { client = TSocketVersionizer.CreateTcpClient(); client.ReceiveTimeout = client.SendTimeout = timeout; client.Client.NoDelay = true; } /// /// Sets Send / Recv Timeout for IO /// public int Timeout { set { this.client.ReceiveTimeout = this.client.SendTimeout = this.timeout = value; } } /// /// Gets the TCP client. /// public TcpClient TcpClient { get { return client; } } /// /// Gets the host. /// public string Host { get { return host; } } /// /// Gets the port. /// public int Port { get { return port; } } /// /// Gets a value indicating whether TCP Client is Cpen /// public override bool IsOpen { get { if (this.client == null) { return false; } return this.client.Connected; } } /// /// Validates the certificates!
///
/// The sender-object. /// The used certificate. /// The certificate chain. /// An enum, which lists all the errors from the .NET certificate check. /// private bool DefaultCertificateValidator(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslValidationErrors) { return (sslValidationErrors == SslPolicyErrors.None); } /// /// Connects to the host and starts the routine, which sets up the TLS /// 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(); } /// /// Creates a TLS-stream and lays it over the existing socket /// public void setupTLS() { RemoteCertificateValidationCallback validator = this.certValidator ?? DefaultCertificateValidator; if (this.localCertificateSelectionCallback != null) { this.secureStream = new SslStream( this.client.GetStream(), false, validator, this.localCertificateSelectionCallback ); } else { this.secureStream = new SslStream( this.client.GetStream(), false, validator ); } try { if (isServer) { // Server authentication this.secureStream.AuthenticateAsServer(this.certificate, this.certValidator != null, sslProtocols, true); } else { // Client authentication X509CertificateCollection certs = certificate != null ? new X509CertificateCollection { certificate } : new X509CertificateCollection(); this.secureStream.AuthenticateAsClient(host, certs, sslProtocols, true); } } catch (Exception) { this.Close(); throw; } inputStream = this.secureStream; outputStream = this.secureStream; } /// /// Closes the SSL Socket /// 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; } } } }