SSLStream C # (клиент и сервер не могут общаться, потому что они не имеют общего алгоритма) - PullRequest
0 голосов
/ 15 февраля 2019

Я пытаюсь создать простое соединение TCP (TLS1.2) TCP.Я использую самоподписанный сертификат, сгенерированный кодом.Я продолжаю получать сообщение об ошибке.

Сбой вызова SSPI, см. Внутреннее исключение.---> System.ComponentModel.Win32Exception: клиент и сервер не могут обмениваться данными, поскольку у них нет общего алгоритма

Ниже приводится мой полный код .net 4.7.1.:

using System;
using System.IO;
using System.Net;
using System.Net.Security;
using System.Net.Sockets;
using System.Security.Authentication;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using System.Threading;

namespace SSLStreamBasic
{
    internal class Program
    {
        private static void Main()
        {
            SecureTcpServer server = null;
            SecureTcpClient client = null;
            try
            {
                int port = 8889;
                RemoteCertificateValidationCallback certValidationCallback = IgnoreCertificateErrorsCallback;
                X509Certificate serverCert = Constants.PrivateKey;
                server = new SecureTcpServer(port, serverCert,
                    OnServerConnectionAvailable);
                server.StartListening();
                client = new SecureTcpClient(OnClientConnectionAvailable,
                    certValidationCallback);
                client.StartConnecting("localhost", new IPEndPoint(IPAddress.Loopback, port));
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex);
            }
            //sleep to avoid printing this text until after the callbacks have been invoked.
            Thread.Sleep(4000);
            Console.WriteLine("Press any key to continue...");
            Console.ReadKey();
            if (server != null)
                server.Dispose();
            if (client != null)
                client.Dispose();
        }

        private static void OnServerConnectionAvailable(object sender, SecureConnectionResults args)
        {
            if (args.AsyncException != null)
            {
                Console.WriteLine(args.AsyncException);
                return;
            }
            SslStream stream = args.SecureStream;
           Console.WriteLine("Server Connection secured: " + stream.IsAuthenticated);
            StreamWriter writer = new StreamWriter(stream);
            writer.AutoFlush = true;

            writer.WriteLine("Hello from server!");

            StreamReader reader = new StreamReader(stream);
            string line = reader.ReadLine();
            Console.WriteLine("Server Recieved: '{0}'", line == null ? "<NULL>" : line);

            writer.Close();
            reader.Close();
            stream.Close();
        }

        private static void OnClientConnectionAvailable(object sender, SecureConnectionResults args)
        {
            if (args.AsyncException != null)
            {
                Console.WriteLine(args.AsyncException);
                return;
            }
            SslStream stream = args.SecureStream;

            Console.WriteLine("Client Connection secured: " + stream.IsAuthenticated);

            StreamWriter writer = new StreamWriter(stream);
            writer.AutoFlush = true;

            writer.WriteLine("Hello from client!");

            StreamReader reader = new StreamReader(stream);
            string line = reader.ReadLine();
            Console.WriteLine("Client Recieved: '{0}'", line == null ? "<NULL>" : line);

            writer.Close();
            reader.Close();
            stream.Close();
        }

        private static bool IgnoreCertificateErrorsCallback(object sender,
            X509Certificate certificate,
            X509Chain chain,
            SslPolicyErrors sslPolicyErrors)
        {
            if (sslPolicyErrors != SslPolicyErrors.None)
            {

                Console.WriteLine("IgnoreCertificateErrorsCallback: {0}", sslPolicyErrors);
                //you should implement different logic here...

                if ((sslPolicyErrors & SslPolicyErrors.RemoteCertificateChainErrors) != 0)
                {
                    foreach (X509ChainStatus chainStatus in chain.ChainStatus)
                    {
                        Console.WriteLine("\t" + chainStatus.Status);
                    }
                }
            }

            //returning true tells the SslStream object you don't care about any errors.
            return true;
        }
    }

    public class SecureTcpClient : IDisposable
    {
        private X509CertificateCollection _clientCertificates;
        private readonly RemoteCertificateValidationCallback _certValidationCallback;
        private readonly SecureConnectionResultsCallback _connectionCallback;
        private bool _checkCertificateRevocation;

        private readonly AsyncCallback _onConnected;
        private readonly AsyncCallback _onAuthenticateAsClient;
        private TcpClient _client;
        private IPEndPoint _remoteEndPoint;
        private string _remoteHostName;
        private readonly SslProtocols _protocols;
        private int _disposed;

        public SecureTcpClient(SecureConnectionResultsCallback callback)
            : this(callback, null, SslProtocols.Tls12)
        {
        }
        public SecureTcpClient(SecureConnectionResultsCallback callback,
            RemoteCertificateValidationCallback certValidationCallback)
            : this(callback, certValidationCallback, SslProtocols.Tls12)
        {
        }

        public SecureTcpClient(SecureConnectionResultsCallback callback,
            RemoteCertificateValidationCallback certValidationCallback, SslProtocols sslProtocols)
        {
            if (callback == null)
                throw new ArgumentNullException("callback");

            _onConnected = OnConnected;
            _onAuthenticateAsClient = OnAuthenticateAsClient;

            this._certValidationCallback = certValidationCallback;
            _connectionCallback = callback;
            _protocols = sslProtocols;
            _disposed = 0;
        }

        ~SecureTcpClient()
        {
            Dispose();
        }

        public bool CheckCertificateRevocation
        {
            get { return _checkCertificateRevocation; }
            set { _checkCertificateRevocation = value; }
        }

        public void StartConnecting(string remoteHostName, IPEndPoint remoteEndPoint)
        {
            StartConnecting(remoteHostName, remoteEndPoint, null);
        }

        public void StartConnecting(string remoteHostName, IPEndPoint remoteEndPoint,
            X509CertificateCollection clientCertificates)
        {

            if (string.IsNullOrEmpty(remoteHostName))
                throw new ArgumentException("Value cannot be null or empty", "remoteHostName");

            if (remoteEndPoint == null)
                throw new ArgumentNullException("remoteEndPoint");

            Console.WriteLine("Client connecting to: {0}", remoteEndPoint);

            this._clientCertificates = clientCertificates;
            this._remoteHostName = remoteHostName;
            this._remoteEndPoint = remoteEndPoint;

            if (_client != null)
                _client.Close();

            _client = new TcpClient(remoteEndPoint.AddressFamily);


            _client.BeginConnect(remoteEndPoint.Address,
                remoteEndPoint.Port,
                _onConnected, null);
        }

        public void Close()
        {
            Dispose();
        }

        private void OnConnected(IAsyncResult result)
        {
            SslStream sslStream = null;

            try
            {
                bool leaveStreamOpen = false;//close the socket when done

                if (_certValidationCallback != null)
                    sslStream = new SslStream(_client.GetStream(), leaveStreamOpen, _certValidationCallback);

                else
                    sslStream = new SslStream(_client.GetStream(), leaveStreamOpen);


                sslStream.BeginAuthenticateAsClient(_remoteHostName,
                    _clientCertificates,
                    _protocols,
                    _checkCertificateRevocation,
                    _onAuthenticateAsClient,
                    sslStream);
            }
            catch (Exception ex)
            {
                if (sslStream != null)
                {
                    sslStream.Dispose();
                    sslStream = null;
                }

                _connectionCallback(this, new SecureConnectionResults(ex));
            }
        }

        private void OnAuthenticateAsClient(IAsyncResult result)
        {
            SslStream sslStream = null;
            try
            {
                sslStream = result.AsyncState as SslStream;
                sslStream.EndAuthenticateAsClient(result);

                _connectionCallback(this, new SecureConnectionResults(sslStream));
            }
            catch (Exception ex)
            {
                if (sslStream != null)
                {
                    sslStream.Dispose();
                    sslStream = null;
                }

                _connectionCallback(this, new SecureConnectionResults(ex));
            }
        }

        public void Dispose()
        {
            if (Interlocked.Increment(ref _disposed) == 1)
            {
                if (_client != null)
                {
                    _client.Close();
                    _client = null;
                }
                GC.SuppressFinalize(this);
            }
        }
    }
    public static class Constants
    {
        public static X509Certificate2 PrivateKey => CreateCertificate();
        public static X509Certificate2 CreateCertificate()
        {
            var ecdsa = ECDsa.Create(); // generate asymmetric key pair
            var req = new CertificateRequest("CN=foobar", ecdsa, HashAlgorithmName.SHA512);
            var cert = req.CreateSelfSigned(DateTimeOffset.Now, DateTimeOffset.Now.AddYears(5));
            // Create PFX (PKCS #12) with private key
            var x = cert.Export(X509ContentType.Pfx, "123456789");
            var cer = new X509Certificate2(x, "123456789");
            return cer;

        }
    }
    public delegate void SecureConnectionResultsCallback(object sender, SecureConnectionResults args);
    public class SecureConnectionResults
    {
        private readonly SslStream _secureStream;
        private readonly Exception _asyncException;
        internal SecureConnectionResults(SslStream sslStream)
        {
            _secureStream = sslStream;
        }

        internal SecureConnectionResults(Exception exception)
        {
            _asyncException = exception;
        }

        public Exception AsyncException { get { return _asyncException; } }
        public SslStream SecureStream { get { return _secureStream; } }
    }
    public class SecureTcpServer : IDisposable
    {
        private readonly X509Certificate _serverCert;
        private readonly RemoteCertificateValidationCallback _certValidationCallback;
        private readonly SecureConnectionResultsCallback _connectionCallback;
        private readonly AsyncCallback _onAcceptConnection;
        private readonly AsyncCallback _onAuthenticateAsServer;

        private bool _started;

        private readonly int _listenPort;
        private TcpListener _listenerV4;
        private TcpListener _listenerV6;
        private int _disposed;
        private bool _clientCertificateRequired;
        private bool _checkCertifcateRevocation;
        private SslProtocols _sslProtocols;

        public SecureTcpServer(int listenPort, X509Certificate serverCertificate,
            SecureConnectionResultsCallback callback)
            : this(listenPort, serverCertificate, callback, null)
        {
        }

        public SecureTcpServer(int listenPort, X509Certificate serverCertificate,
            SecureConnectionResultsCallback callback,
            RemoteCertificateValidationCallback certValidationCallback)
        {
            if (listenPort < 0 || listenPort > UInt16.MaxValue)
                throw new ArgumentOutOfRangeException("listenPort");

            if (serverCertificate == null)
                throw new ArgumentNullException("serverCertificate");

            if (callback == null)
                throw new ArgumentNullException("callback");

            _onAcceptConnection = OnAcceptConnection;
            _onAuthenticateAsServer = OnAuthenticateAsServer;

            _serverCert = serverCertificate;
            this._certValidationCallback = certValidationCallback;
            _connectionCallback = callback;
            this._listenPort = listenPort;
            _disposed = 0;
            _checkCertifcateRevocation = false;
            _clientCertificateRequired = false;
            _sslProtocols = SslProtocols.Tls12;
        }

        ~SecureTcpServer()
        {
            Dispose();
        }

        public SslProtocols SslProtocols
        {
            get { return _sslProtocols; }
            set { _sslProtocols = value; }
        }

        public bool CheckCertifcateRevocation
        {
            get { return _checkCertifcateRevocation; }
            set { _checkCertifcateRevocation = value; }
        }


        public bool ClientCertificateRequired
        {
            get { return _clientCertificateRequired; }
            set { _clientCertificateRequired = value; }
        }

        public void StartListening()
        {
            if (_started)
                throw new InvalidOperationException("Already started...");

            IPEndPoint localIp;
            if (Socket.SupportsIPv4 && _listenerV4 == null)
            {
                localIp = new IPEndPoint(IPAddress.Any, _listenPort);
                Console.WriteLine("SecureTcpServer: Started listening on {0}", localIp);
                _listenerV4 = new TcpListener(localIp);
            }

            if (Socket.OSSupportsIPv6 && _listenerV6 == null)
            {
                localIp = new IPEndPoint(IPAddress.IPv6Any, _listenPort);
                Console.WriteLine("SecureTcpServer: Started listening on {0}", localIp);
                _listenerV6 = new TcpListener(localIp);
            }

            if (_listenerV4 != null)
            {
                _listenerV4.Start();
                _listenerV4.BeginAcceptTcpClient(_onAcceptConnection, _listenerV4);
            }

            if (_listenerV6 != null)
            {
                _listenerV6.Start();
                _listenerV6.BeginAcceptTcpClient(_onAcceptConnection, _listenerV6);
            }

            _started = true;
        }

        public void StopListening()
        {
            if (!_started)
                return;

            _started = false;

            if (_listenerV4 != null)
                _listenerV4.Stop();
            if (_listenerV6 != null)
                _listenerV6.Stop();
        }

        private void OnAcceptConnection(IAsyncResult result)
        {
            TcpListener listener = result.AsyncState as TcpListener;
            TcpClient client;
            SslStream sslStream = null;

            try
            {
                if (_started)
                {
                    //start accepting the next connection...
                    listener.BeginAcceptTcpClient(_onAcceptConnection, listener);
                }
                else
                {
                    //someone called Stop() - don't call EndAcceptTcpClient because
                    //it will throw an ObjectDisposedException
                    return;
                }

                //complete the last operation...
                client = listener.EndAcceptTcpClient(result);


                bool leaveStreamOpen = false;//close the socket when done

                if (_certValidationCallback != null)
                    sslStream = new SslStream(client.GetStream(), leaveStreamOpen, _certValidationCallback);
                else
                    sslStream = new SslStream(client.GetStream(), leaveStreamOpen);

                sslStream.BeginAuthenticateAsServer(_serverCert,
                    _clientCertificateRequired,
                    _sslProtocols,
                    _checkCertifcateRevocation,//checkCertifcateRevocation
                    _onAuthenticateAsServer,
                    sslStream);


            }
            catch (Exception ex)
            {
                if (sslStream != null)
                {
                    sslStream.Dispose();
                    sslStream = null;
                }
                _connectionCallback(this, new SecureConnectionResults(ex));
            }
        }

        private void OnAuthenticateAsServer(IAsyncResult result)
        {
            SslStream sslStream = null;
            try
            {
                sslStream = result.AsyncState as SslStream;
                sslStream.EndAuthenticateAsServer(result);
                _connectionCallback(this, new SecureConnectionResults(sslStream));
            }
            catch (Exception ex)
            {
                if (sslStream != null)
                {
                    sslStream.Dispose();
                    sslStream = null;
                }
                _connectionCallback(this, new SecureConnectionResults(ex));
            }
        }

        public void Dispose()
        {
            if (Interlocked.Increment(ref _disposed) == 1)
            {
                if (_listenerV4 != null)
                    _listenerV4.Stop();
                if (_listenerV6 != null)
                    _listenerV6.Stop();

                _listenerV4 = null;
                _listenerV6 = null;

                GC.SuppressFinalize(this);
            }
        }
    }
}
...