public async Task CertificateValidationClientServer_EndToEnd_Ok()
        {
            IPEndPoint endPoint = new IPEndPoint(IPAddress.IPv6Loopback, 0);
            var server = new TcpListener(endPoint);
            server.Start();

            using (var clientConnection = new TcpClient(AddressFamily.InterNetworkV6))
            {
                IPEndPoint serverEndPoint = (IPEndPoint)server.LocalEndpoint;

                Task clientConnect = clientConnection.ConnectAsync(serverEndPoint.Address, serverEndPoint.Port);
                Task<TcpClient> serverAccept = server.AcceptTcpClientAsync();

                Assert.True(
                    Task.WaitAll(
                        new Task[] { clientConnect, serverAccept }, 
                        TestConfiguration.TestTimeoutSeconds * 1000),
                    "Client/Server TCP Connect timed out.");

                using (TcpClient serverConnection = await serverAccept)
                using (SslStream sslClientStream = new SslStream(
                    clientConnection.GetStream(),
                    false,
                    ClientSideRemoteServerCertificateValidation))
                using (SslStream sslServerStream = new SslStream(
                    serverConnection.GetStream(),
                    false,
                    ServerSideRemoteClientCertificateValidation))

                {
                    string serverName = _serverCertificate.GetNameInfo(X509NameType.SimpleName, false);
                    string clientName = _clientCertificate.GetNameInfo(X509NameType.SimpleName, false);

                    var clientCerts = new X509CertificateCollection();
                    clientCerts.Add(_clientCertificate);

                    Task clientAuthentication = sslClientStream.AuthenticateAsClientAsync(
                        serverName,
                        clientCerts,
                        TestConfiguration.DefaultSslProtocols,
                        false);

                    Task serverAuthentication = sslServerStream.AuthenticateAsServerAsync(
                        _serverCertificate,
                        true,
                        TestConfiguration.DefaultSslProtocols,
                        false);

                    Assert.True(
                        Task.WaitAll(
                            new Task[] { clientAuthentication, serverAuthentication }, 
                            TestConfiguration.TestTimeoutSeconds * 1000),
                        "Client/Server Authentication timed out.");
                }
            }
        }
Пример #2
0
        public async Task SslStream_StreamToStream_HandshakeAlert_Ok()
        {
            VirtualNetwork network = new VirtualNetwork();

            using (var clientStream = new VirtualNetworkStream(network, isServer: false))
            using (var serverStream = new VirtualNetworkStream(network, isServer: true))
            using (var client = new SslStream(clientStream, true, AllowAnyServerCertificate))
            using (var server = new SslStream(serverStream, true, FailClientCertificate))
            using (X509Certificate2 certificate = Configuration.Certificates.GetServerCertificate())
            {
                Task serverAuth = server.AuthenticateAsServerAsync(certificate);
                await client.AuthenticateAsClientAsync(certificate.GetNameInfo(X509NameType.SimpleName, false));

                byte[] buffer = new byte[1024];

                // Schannel semantics require that Decrypt is called to receive an alert.
                await client.WriteAsync(buffer, 0, buffer.Length);
                var exception = await Assert.ThrowsAsync<IOException>(() => client.ReadAsync(buffer, 0, buffer.Length));

                Assert.IsType<Win32Exception>(exception.InnerException);
                var win32ex = (Win32Exception)exception.InnerException;

                // The Schannel HResults for each alert are documented here: 
                // https://msdn.microsoft.com/en-us/library/windows/desktop/dd721886(v=vs.85).aspx
                Assert.Equal(SEC_E_CERT_UNKNOWN, (uint)win32ex.NativeErrorCode);

                await Assert.ThrowsAsync<AuthenticationException>(() => serverAuth);

                await Assert.ThrowsAsync<AuthenticationException>(() => server.WriteAsync(buffer, 0, buffer.Length));
                await Assert.ThrowsAsync<AuthenticationException>(() => server.ReadAsync(buffer, 0, buffer.Length));
            }
        }
Пример #3
0
        public async Task SslStream_StreamToStream_ServerInitiatedCloseNotify_Ok()
        {
            VirtualNetwork network = new VirtualNetwork();

            using (var clientStream = new VirtualNetworkStream(network, isServer: false))
            using (var serverStream = new VirtualNetworkStream(network, isServer: true))
            using (var client = new SslStream(clientStream, true, AllowAnyServerCertificate))
            using (var server = new SslStream(serverStream))
            using (X509Certificate2 certificate = Configuration.Certificates.GetServerCertificate())
            {
                var handshake = new Task[2];

                handshake[0] = server.AuthenticateAsServerAsync(certificate);
                handshake[1] = client.AuthenticateAsClientAsync(certificate.GetNameInfo(X509NameType.SimpleName, false));

                await Task.WhenAll(handshake).TimeoutAfter(TestConfiguration.PassingTestTimeoutMilliseconds);

                var readBuffer = new byte[1024];

                await server.ShutdownAsync();
                int bytesRead = await client.ReadAsync(readBuffer, 0, readBuffer.Length);
                // close_notify received by the client.
                Assert.Equal(0, bytesRead);

                await client.ShutdownAsync();
                bytesRead = await server.ReadAsync(readBuffer, 0, readBuffer.Length);
                // close_notify received by the server.
                Assert.Equal(0, bytesRead);
            }
        }
Пример #4
0
        public async void SslStream_SendReceiveOverNetworkStream_Ok()
        {
            TcpListener listener = new TcpListener(IPAddress.Any, 0);

            using (X509Certificate2 serverCertificate = Configuration.Certificates.GetServerCertificate())
            using (TcpClient client = new TcpClient())
            {
                listener.Start();

                Task clientConnectTask = client.ConnectAsync(IPAddress.Loopback, ((IPEndPoint)listener.LocalEndpoint).Port);
                Task<TcpClient> listenerAcceptTask = listener.AcceptTcpClientAsync();

                await Task.WhenAll(clientConnectTask, listenerAcceptTask);

                TcpClient server = listenerAcceptTask.Result;
                using (SslStream clientStream = new SslStream(
                    client.GetStream(),
                    false,
                    new RemoteCertificateValidationCallback(ValidateServerCertificate),
                    null,
                    EncryptionPolicy.RequireEncryption))
                using (SslStream serverStream = new SslStream(
                    server.GetStream(),
                    false,
                    null,
                    null,
                    EncryptionPolicy.RequireEncryption))
                {

                    Task clientAuthenticationTask = clientStream.AuthenticateAsClientAsync(
                        serverCertificate.GetNameInfo(X509NameType.SimpleName, false),
                        null,
                        SslProtocols.Tls12,
                        false);

                    Task serverAuthenticationTask = serverStream.AuthenticateAsServerAsync(
                        serverCertificate,
                        false,
                        SslProtocols.Tls12,
                        false);

                    await Task.WhenAll(clientAuthenticationTask, serverAuthenticationTask);

                    byte[] readBuffer = new byte[256];
                    Task<int> readTask = clientStream.ReadAsync(readBuffer, 0, readBuffer.Length);

                    byte[] writeBuffer = new byte[256];
                    Task writeTask = clientStream.WriteAsync(writeBuffer, 0, writeBuffer.Length);

                    bool result = Task.WaitAll(
                        new Task[1] { writeTask }, 
                        TestConfiguration.PassingTestTimeoutMilliseconds);

                    Assert.True(result, "WriteAsync timed-out.");
                }
            }
        }
Пример #5
0
        public void SslStream_StreamToStream_Authentication_Success()
        {
            VirtualNetwork network = new VirtualNetwork();

            using (var clientStream = new VirtualNetworkStream(network, isServer: false))
            using (var serverStream = new VirtualNetworkStream(network, isServer: true))
            using (var client = new SslStream(clientStream, false, AllowAnyServerCertificate))
            using (var server = new SslStream(serverStream))
            {
                X509Certificate2 certificate = TestConfiguration.GetServerCertificate();
                Task[] auth = new Task[2];
                auth[0] = client.AuthenticateAsClientAsync(certificate.GetNameInfo(X509NameType.SimpleName, false));
                auth[1] = server.AuthenticateAsServerAsync(certificate);

                bool finished = Task.WaitAll(auth, TestConfiguration.PassingTestTimeoutMilliseconds);
                Assert.True(finished, "Handshake completed in the allotted time");
            }
        }
        public void SslStream_StreamToStream_Authentication_Success()
        {
            MockNetwork network = new MockNetwork();

            using (var clientStream = new FakeNetworkStream(false, network))
            using (var serverStream = new FakeNetworkStream(true, network))
            using (var client = new SslStream(clientStream, false, AllowAnyServerCertificate))
            using (var server = new SslStream(serverStream))
            {
                X509Certificate2 certificate = TestConfiguration.GetServerCertificate();
                Task[] auth = new Task[2];
                auth[0] = client.AuthenticateAsClientAsync(certificate.Subject);
                auth[1] = server.AuthenticateAsServerAsync(certificate);

                bool finished = Task.WaitAll(auth, TimeSpan.FromSeconds(3));
                Assert.True(finished, "Handshake completed in the allotted time");
            }
        }
        public void SslStream_StreamToStream_Authentication_IncorrectServerName_Fail()
        {
            MockNetwork network = new MockNetwork();

            using (var clientStream = new FakeNetworkStream(false, network))
            using (var serverStream = new FakeNetworkStream(true, network))
            using (var client = new SslStream(clientStream))
            using (var server = new SslStream(serverStream))
            {
                Task[] auth = new Task[2];
                auth[0] = client.AuthenticateAsClientAsync("incorrectServer");
                auth[1] = server.AuthenticateAsServerAsync(TestConfiguration.GetServerCertificate());

                Assert.Throws<AuthenticationException>(() =>
                {
                    auth[0].GetAwaiter().GetResult();
                });

                auth[1].GetAwaiter().GetResult();
            }
        }
        public void SslStream_StreamToStream_Authentication_IncorrectServerName_Fail()
        {
            VirtualNetwork network = new VirtualNetwork();

            using (var clientStream = new VirtualNetworkStream(network, isServer: false))
            using (var serverStream = new VirtualNetworkStream(network, isServer: true))
            using (var client = new SslStream(clientStream))
            using (var server = new SslStream(serverStream))
            using (var certificate = Configuration.Certificates.GetServerCertificate())
            {
                Task[] auth = new Task[2];
                auth[0] = client.AuthenticateAsClientAsync("incorrectServer");
                auth[1] = server.AuthenticateAsServerAsync(certificate);

                Assert.Throws<AuthenticationException>(() =>
                {
                    auth[0].GetAwaiter().GetResult();
                });

                auth[1].GetAwaiter().GetResult();
            }
        }
Пример #9
0
        /// <summary>
        ///     This is called when client is aware of proxy
        ///     So for HTTPS requests client would send CONNECT header to negotiate a secure tcp tunnel via proxy
        /// </summary>
        /// <param name="endPoint">The explicit endpoint.</param>
        /// <param name="clientConnection">The client connection.</param>
        /// <returns>The task.</returns>
        private async Task handleClient(ExplicitProxyEndPoint endPoint, TcpClientConnection clientConnection)
        {
            var cancellationTokenSource = new CancellationTokenSource();
            var cancellationToken       = cancellationTokenSource.Token;

            var clientStream       = new CustomBufferedStream(clientConnection.GetStream(), BufferPool, BufferSize);
            var clientStreamWriter = new HttpResponseWriter(clientStream, BufferPool, BufferSize);

            Task <TcpServerConnection> prefetchConnectionTask = null;
            bool closeServerConnection = false;
            bool calledRequestHandler  = false;

            try
            {
                string connectHostname = null;
                TunnelConnectSessionEventArgs connectArgs = null;


                // Client wants to create a secure tcp tunnel (probably its a HTTPS or Websocket request)
                if (await HttpHelper.IsConnectMethod(clientStream) == 1)
                {
                    // read the first line HTTP command
                    string httpCmd = await clientStream.ReadLineAsync(cancellationToken);

                    if (string.IsNullOrEmpty(httpCmd))
                    {
                        return;
                    }

                    Request.ParseRequestLine(httpCmd, out string _, out string httpUrl, out var version);

                    var httpRemoteUri = new Uri("http://" + httpUrl);
                    connectHostname = httpRemoteUri.Host;

                    var connectRequest = new ConnectRequest
                    {
                        RequestUri  = httpRemoteUri,
                        OriginalUrl = httpUrl,
                        HttpVersion = version
                    };

                    await HeaderParser.ReadHeaders(clientStream, connectRequest.Headers, cancellationToken);

                    connectArgs = new TunnelConnectSessionEventArgs(this, endPoint, connectRequest,
                                                                    cancellationTokenSource);
                    connectArgs.ProxyClient.ClientConnection = clientConnection;
                    connectArgs.ProxyClient.ClientStream     = clientStream;

                    await endPoint.InvokeBeforeTunnelConnectRequest(this, connectArgs, ExceptionFunc);

                    // filter out excluded host names
                    bool decryptSsl = endPoint.DecryptSsl && connectArgs.DecryptSsl;

                    if (connectArgs.DenyConnect)
                    {
                        if (connectArgs.WebSession.Response.StatusCode == 0)
                        {
                            connectArgs.WebSession.Response = new Response
                            {
                                HttpVersion       = HttpHeader.Version11,
                                StatusCode        = (int)HttpStatusCode.Forbidden,
                                StatusDescription = "Forbidden"
                            };
                        }

                        // send the response
                        await clientStreamWriter.WriteResponseAsync(connectArgs.WebSession.Response,
                                                                    cancellationToken : cancellationToken);

                        return;
                    }

                    if (await checkAuthorization(connectArgs) == false)
                    {
                        await endPoint.InvokeBeforeTunnectConnectResponse(this, connectArgs, ExceptionFunc);

                        // send the response
                        await clientStreamWriter.WriteResponseAsync(connectArgs.WebSession.Response,
                                                                    cancellationToken : cancellationToken);

                        return;
                    }

                    // write back successfull CONNECT response
                    var response = ConnectResponse.CreateSuccessfullConnectResponse(version);

                    // Set ContentLength explicitly to properly handle HTTP 1.0
                    response.ContentLength = 0;
                    response.Headers.FixProxyHeaders();
                    connectArgs.WebSession.Response = response;

                    await clientStreamWriter.WriteResponseAsync(response, cancellationToken : cancellationToken);

                    var clientHelloInfo = await SslTools.PeekClientHello(clientStream, BufferPool, cancellationToken);

                    bool isClientHello = clientHelloInfo != null;
                    if (isClientHello)
                    {
                        connectRequest.ClientHelloInfo = clientHelloInfo;
                    }

                    await endPoint.InvokeBeforeTunnectConnectResponse(this, connectArgs, ExceptionFunc, isClientHello);

                    if (decryptSsl && isClientHello)
                    {
                        connectRequest.RequestUri = new Uri("https://" + httpUrl);

                        bool http2Supported = false;

                        var alpn = clientHelloInfo.GetAlpn();
                        if (alpn != null && alpn.Contains(SslApplicationProtocol.Http2))
                        {
                            // test server HTTP/2 support
                            // todo: this is a hack, because Titanium does not support HTTP protocol changing currently
                            var connection = await tcpConnectionFactory.GetServerConnection(this, connectArgs,
                                                                                            isConnect : true, applicationProtocols : SslExtensions.Http2ProtocolAsList,
                                                                                            noCache : true, cancellationToken : cancellationToken);

                            http2Supported = connection.NegotiatedApplicationProtocol == SslApplicationProtocol.Http2;

                            //release connection back to pool intead of closing when connection pool is enabled.
                            await tcpConnectionFactory.Release(connection, true);
                        }

                        SslStream sslStream = null;

                        //don't pass cancellation token here
                        //it could cause floating server connections when client exits
                        prefetchConnectionTask = tcpConnectionFactory.GetServerConnection(this, connectArgs,
                                                                                          isConnect: true, applicationProtocols: null, noCache: false,
                                                                                          cancellationToken: CancellationToken.None);

                        try
                        {
                            sslStream = new SslStream(clientStream);

                            string certName = HttpHelper.GetWildCardDomainName(connectHostname);

                            var certificate = endPoint.GenericCertificate ??
                                              await CertificateManager.CreateCertificateAsync(certName);

                            // Successfully managed to authenticate the client using the fake certificate
                            var options = new SslServerAuthenticationOptions();
                            if (http2Supported)
                            {
                                options.ApplicationProtocols = clientHelloInfo.GetAlpn();
                                if (options.ApplicationProtocols == null || options.ApplicationProtocols.Count == 0)
                                {
                                    options.ApplicationProtocols = SslExtensions.Http11ProtocolAsList;
                                }
                            }

                            options.ServerCertificate              = certificate;
                            options.ClientCertificateRequired      = false;
                            options.EnabledSslProtocols            = SupportedSslProtocols;
                            options.CertificateRevocationCheckMode = X509RevocationMode.NoCheck;
                            await sslStream.AuthenticateAsServerAsync(options, cancellationToken);

#if NETCOREAPP2_1
                            clientConnection.NegotiatedApplicationProtocol = sslStream.NegotiatedApplicationProtocol;
#endif

                            // HTTPS server created - we can now decrypt the client's traffic
                            clientStream       = new CustomBufferedStream(sslStream, BufferPool, BufferSize);
                            clientStreamWriter = new HttpResponseWriter(clientStream, BufferPool, BufferSize);
                        }
                        catch (Exception e)
                        {
                            sslStream?.Dispose();
                            throw new ProxyConnectException(
                                      $"Could'nt authenticate client '{connectHostname}' with fake certificate.", e, connectArgs);
                        }

                        if (await HttpHelper.IsConnectMethod(clientStream) == -1)
                        {
                            decryptSsl = false;
                        }
                    }

                    if (cancellationTokenSource.IsCancellationRequested)
                    {
                        throw new Exception("Session was terminated by user.");
                    }

                    // Hostname is excluded or it is not an HTTPS connect
                    if (!decryptSsl || !isClientHello)
                    {
                        // create new connection to server.
                        // If we detected that client tunnel CONNECTs without SSL by checking for empty client hello then
                        // this connection should not be HTTPS.
                        var connection = await tcpConnectionFactory.GetServerConnection(this, connectArgs,
                                                                                        isConnect : true, applicationProtocols : SslExtensions.Http2ProtocolAsList,
                                                                                        noCache : true, cancellationToken : cancellationToken);

                        try
                        {
                            if (isClientHello)
                            {
                                int available = clientStream.Available;
                                if (available > 0)
                                {
                                    // send the buffered data
                                    var data = BufferPool.GetBuffer(BufferSize);

                                    try
                                    {
                                        await clientStream.ReadAsync(data, 0, available, cancellationToken);

                                        // clientStream.Available sbould be at most BufferSize because it is using the same buffer size
                                        await connection.StreamWriter.WriteAsync(data, 0, available, true, cancellationToken);
                                    }
                                    finally
                                    {
                                        BufferPool.ReturnBuffer(data);
                                    }
                                }

                                var serverHelloInfo = await SslTools.PeekServerHello(connection.Stream, BufferPool, cancellationToken);

                                ((ConnectResponse)connectArgs.WebSession.Response).ServerHelloInfo = serverHelloInfo;
                            }

                            await TcpHelper.SendRaw(clientStream, connection.Stream, BufferPool, BufferSize,
                                                    (buffer, offset, count) => { connectArgs.OnDataSent(buffer, offset, count); },
                                                    (buffer, offset, count) => { connectArgs.OnDataReceived(buffer, offset, count); },
                                                    connectArgs.CancellationTokenSource, ExceptionFunc);
                        }
                        finally
                        {
                            await tcpConnectionFactory.Release(connection, true);
                        }

                        return;
                    }
                }

                if (connectArgs != null && await HttpHelper.IsPriMethod(clientStream) == 1)
                {
                    // todo
                    string httpCmd = await clientStream.ReadLineAsync(cancellationToken);

                    if (httpCmd == "PRI * HTTP/2.0")
                    {
                        // HTTP/2 Connection Preface
                        string line = await clientStream.ReadLineAsync(cancellationToken);

                        if (line != string.Empty)
                        {
                            throw new Exception($"HTTP/2 Protocol violation. Empty string expected, '{line}' received");
                        }

                        line = await clientStream.ReadLineAsync(cancellationToken);

                        if (line != "SM")
                        {
                            throw new Exception($"HTTP/2 Protocol violation. 'SM' expected, '{line}' received");
                        }

                        line = await clientStream.ReadLineAsync(cancellationToken);

                        if (line != string.Empty)
                        {
                            throw new Exception($"HTTP/2 Protocol violation. Empty string expected, '{line}' received");
                        }

                        var connection = await tcpConnectionFactory.GetServerConnection(this, connectArgs,
                                                                                        isConnect : true, applicationProtocols : SslExtensions.Http2ProtocolAsList,
                                                                                        noCache : true, cancellationToken : cancellationToken);

                        try
                        {
                            await connection.StreamWriter.WriteLineAsync("PRI * HTTP/2.0", cancellationToken);

                            await connection.StreamWriter.WriteLineAsync(cancellationToken);

                            await connection.StreamWriter.WriteLineAsync("SM", cancellationToken);

                            await connection.StreamWriter.WriteLineAsync(cancellationToken);

#if NETCOREAPP2_1
                            await Http2Helper.SendHttp2(clientStream, connection.Stream, BufferSize,
                                                        (buffer, offset, count) => { connectArgs.OnDataSent(buffer, offset, count); },
                                                        (buffer, offset, count) => { connectArgs.OnDataReceived(buffer, offset, count); },
                                                        connectArgs.CancellationTokenSource, clientConnection.Id, ExceptionFunc);
#endif
                        }
                        finally
                        {
                            await tcpConnectionFactory.Release(connection, true);
                        }
                    }
                }

                calledRequestHandler = true;
                // Now create the request
                await handleHttpSessionRequest(endPoint, clientConnection, clientStream, clientStreamWriter,
                                               cancellationTokenSource, connectHostname, connectArgs?.WebSession.ConnectRequest, prefetchConnectionTask);
            }
            catch (ProxyException e)
            {
                closeServerConnection = true;
                onException(clientStream, e);
            }
            catch (IOException e)
            {
                closeServerConnection = true;
                onException(clientStream, new Exception("Connection was aborted", e));
            }
            catch (SocketException e)
            {
                closeServerConnection = true;
                onException(clientStream, new Exception("Could not connect", e));
            }
            catch (Exception e)
            {
                closeServerConnection = true;
                onException(clientStream, new Exception("Error occured in whilst handling the client", e));
            }
            finally
            {
                if (!calledRequestHandler)
                {
                    await tcpConnectionFactory.Release(prefetchConnectionTask, closeServerConnection);
                }

                clientStream.Dispose();

                if (!cancellationTokenSource.IsCancellationRequested)
                {
                    cancellationTokenSource.Cancel();
                }
            }
        }
        /// <summary>
        ///     This is called when this proxy acts as a reverse proxy (like a real http server).
        ///     So for HTTPS requests we would start SSL negotiation right away without expecting a CONNECT request from client
        /// </summary>
        /// <param name="endPoint">The transparent endpoint.</param>
        /// <param name="clientConnection">The client connection.</param>
        /// <returns></returns>
        private async Task handleClient(TransparentProxyEndPoint endPoint, TcpClientConnection clientConnection)
        {
            var cancellationTokenSource = new CancellationTokenSource();
            var cancellationToken       = cancellationTokenSource.Token;

            var clientStream = new HttpClientStream(clientConnection.GetStream(), BufferPool);

            SslStream?sslStream = null;

            try
            {
                var clientHelloInfo = await SslTools.PeekClientHello(clientStream, BufferPool, cancellationToken);

                string?httpsHostName = null;

                if (clientHelloInfo != null)
                {
                    httpsHostName = clientHelloInfo.GetServerName() ?? endPoint.GenericCertificateName;

                    var args = new BeforeSslAuthenticateEventArgs(cancellationTokenSource, httpsHostName);

                    await endPoint.InvokeBeforeSslAuthenticate(this, args, ExceptionFunc);

                    if (cancellationTokenSource.IsCancellationRequested)
                    {
                        throw new Exception("Session was terminated by user.");
                    }

                    if (endPoint.DecryptSsl && args.DecryptSsl)
                    {
                        clientConnection.SslProtocol = clientHelloInfo.SslProtocol;

                        // do client authentication using certificate
                        X509Certificate2?certificate = null;
                        try
                        {
                            sslStream = new SslStream(clientStream, false);

                            string certName = HttpHelper.GetWildCardDomainName(httpsHostName);
                            certificate = endPoint.GenericCertificate ??
                                          await CertificateManager.CreateServerCertificate(certName);

                            // Successfully managed to authenticate the client using the certificate
                            await sslStream.AuthenticateAsServerAsync(certificate, false, SslProtocols.Tls, false);

                            // HTTPS server created - we can now decrypt the client's traffic
                            clientStream = new HttpClientStream(sslStream, BufferPool);
                            sslStream    = null; // clientStream was created, no need to keep SSL stream reference
                        }
                        catch (Exception e)
                        {
                            var certName = certificate?.GetNameInfo(X509NameType.SimpleName, false);
                            var session  = new SessionEventArgs(this, endPoint, clientConnection, clientStream, null,
                                                                cancellationTokenSource);
                            throw new ProxyConnectException(
                                      $"Couldn't authenticate host '{httpsHostName}' with certificate '{certName}'.", e, session);
                        }
                    }
                    else
                    {
                        var connection = await tcpConnectionFactory.GetServerConnection(httpsHostName, endPoint.Port,
                                                                                        HttpHeader.VersionUnknown, false, null,
                                                                                        true, this, null, UpStreamEndPoint,
                                                                                        UpStreamHttpsProxy, true, cancellationToken);

                        try
                        {
                            int available = clientStream.Available;

                            if (available > 0)
                            {
                                // send the buffered data
                                var data = BufferPool.GetBuffer();
                                try
                                {
                                    // clientStream.Available should be at most BufferSize because it is using the same buffer size
                                    await clientStream.ReadAsync(data, 0, available, cancellationToken);

                                    await connection.Stream.WriteAsync(data, 0, available, true, cancellationToken);
                                }
                                finally
                                {
                                    BufferPool.ReturnBuffer(data);
                                }
                            }

                            if (!clientStream.IsClosed && !connection.Stream.IsClosed)
                            {
                                await TcpHelper.SendRaw(clientStream, connection.Stream, BufferPool,
                                                        null, null, cancellationTokenSource, ExceptionFunc);
                            }
                        }
                        finally
                        {
                            await tcpConnectionFactory.Release(connection, true);
                        }

                        return;
                    }
                }

                // HTTPS server created - we can now decrypt the client's traffic
                // Now create the request
                await handleHttpSessionRequest(endPoint, clientConnection, clientStream, cancellationTokenSource);
            }
            catch (ProxyException e)
            {
                onException(clientStream, e);
            }
            catch (IOException e)
            {
                onException(clientStream, new Exception("Connection was aborted", e));
            }
            catch (SocketException e)
            {
                onException(clientStream, new Exception("Could not connect", e));
            }
            catch (Exception e)
            {
                onException(clientStream, new Exception("Error occured in whilst handling the client", e));
            }
            finally
            {
                sslStream?.Dispose();
                clientStream.Dispose();
            }
        }
        public async Task CertificateValidationClientServer_EndToEnd_Ok(bool useClientSelectionCallback)
        {
            IPEndPoint endPoint = new IPEndPoint(IPAddress.Loopback, 0);
            var        server   = new TcpListener(endPoint);

            server.Start();

            _clientCertificateRemovedByFilter = false;

            if (PlatformDetection.IsWindows7 &&
                !useClientSelectionCallback &&
                !Capability.IsTrustedRootCertificateInstalled())
            {
                // https://technet.microsoft.com/en-us/library/hh831771.aspx#BKMK_Changes2012R2
                // Starting with Windows 8, the "Management of trusted issuers for client authentication" has changed:
                // The behavior to send the Trusted Issuers List by default is off.
                //
                // In Windows 7 the Trusted Issuers List is sent within the Server Hello TLS record. This list is built
                // by the server using certificates from the Trusted Root Authorities certificate store.
                // The client side will use the Trusted Issuers List, if not empty, to filter proposed certificates.
                _clientCertificateRemovedByFilter = true;
            }

            using (var clientConnection = new TcpClient())
            {
                IPEndPoint serverEndPoint = (IPEndPoint)server.LocalEndpoint;

                Task             clientConnect = clientConnection.ConnectAsync(serverEndPoint.Address, serverEndPoint.Port);
                Task <TcpClient> serverAccept  = server.AcceptTcpClientAsync();

                await TestConfiguration.WhenAllOrAnyFailedWithTimeout(clientConnect, serverAccept);

                LocalCertificateSelectionCallback clientCertCallback = null;

                if (useClientSelectionCallback)
                {
                    clientCertCallback = ClientCertSelectionCallback;
                }

                using (TcpClient serverConnection = await serverAccept)
                    using (SslStream sslClientStream = new SslStream(
                               clientConnection.GetStream(),
                               false,
                               ClientSideRemoteServerCertificateValidation,
                               clientCertCallback))
                        using (SslStream sslServerStream = new SslStream(
                                   serverConnection.GetStream(),
                                   false,
                                   ServerSideRemoteClientCertificateValidation))

                        {
                            string serverName  = _serverCertificate.GetNameInfo(X509NameType.SimpleName, false);
                            var    clientCerts = new X509CertificateCollection();

                            if (!useClientSelectionCallback)
                            {
                                clientCerts.Add(_clientCertificate);
                            }

                            Task clientAuthentication = sslClientStream.AuthenticateAsClientAsync(
                                serverName,
                                clientCerts,
                                SslProtocolSupport.DefaultSslProtocols,
                                false);

                            Task serverAuthentication = sslServerStream.AuthenticateAsServerAsync(
                                _serverCertificate,
                                true,
                                SslProtocolSupport.DefaultSslProtocols,
                                false);

                            await TestConfiguration.WhenAllOrAnyFailedWithTimeout(clientAuthentication, serverAuthentication);

                            if (!_clientCertificateRemovedByFilter)
                            {
                                Assert.True(sslClientStream.IsMutuallyAuthenticated, "sslClientStream.IsMutuallyAuthenticated");
                                Assert.True(sslServerStream.IsMutuallyAuthenticated, "sslServerStream.IsMutuallyAuthenticated");

                                Assert.Equal(sslServerStream.RemoteCertificate.Subject, _clientCertificate.Subject);
                            }
                            else
                            {
                                Assert.False(sslClientStream.IsMutuallyAuthenticated, "sslClientStream.IsMutuallyAuthenticated");
                                Assert.False(sslServerStream.IsMutuallyAuthenticated, "sslServerStream.IsMutuallyAuthenticated");

                                Assert.Null(sslServerStream.RemoteCertificate);
                            }

                            Assert.Equal(sslClientStream.RemoteCertificate.Subject, _serverCertificate.Subject);
                        }
            }
        }
        public async Task SslStream_ServerLocalCertificateSelectionCallbackReturnsNull_Throw()
        {
            var selectionCallback = new LocalCertificateSelectionCallback((object sender, string targetHost, X509CertificateCollection localCertificates, X509Certificate remoteCertificate, string[] issuers) =>
            {
                return(null);
            });

            (Stream stream1, Stream stream2) = TestHelper.GetConnectedStreams();
            using (var client = new SslStream(stream1, false, AllowAnyServerCertificate))
                using (var server = new SslStream(stream2, false, null, selectionCallback))
                    using (X509Certificate2 certificate = Configuration.Certificates.GetServerCertificate())
                    {
                        await Assert.ThrowsAsync <NotSupportedException>(async() =>
                                                                         await TestConfiguration.WhenAllOrAnyFailedWithTimeout(client.AuthenticateAsClientAsync(certificate.GetNameInfo(X509NameType.SimpleName, false)), server.AuthenticateAsServerAsync(certificate))
                                                                         );
                    }
        }
        public async Task Authenticate_Precanceled_ThrowsOperationCanceledException()
        {
            var network = new VirtualNetwork();

            using (var clientSslStream = new SslStream(new VirtualNetworkStream(network, isServer: false), false, AllowAnyServerCertificate))
                using (var serverSslStream = new SslStream(new VirtualNetworkStream(network, isServer: true)))
                    using (X509Certificate2 certificate = Configuration.Certificates.GetServerCertificate())
                    {
                        await Assert.ThrowsAnyAsync <OperationCanceledException>(() => clientSslStream.AuthenticateAsClientAsync(new SslClientAuthenticationOptions()
                        {
                            TargetHost = certificate.GetNameInfo(X509NameType.SimpleName, false)
                        }, new CancellationToken(true)));

                        await Assert.ThrowsAnyAsync <OperationCanceledException>(() => serverSslStream.AuthenticateAsServerAsync(new SslServerAuthenticationOptions()
                        {
                            ServerCertificate = certificate
                        }, new CancellationToken(true)));
                    }
        }
Пример #14
0
        /// <summary>
        /// rtmp握手逻辑
        /// HandshakeAsync2
        /// c01->s01->s2->c2
        /// </summary>
        /// <param name="server"></param>
        /// <param name="client_socket"></param>
        /// <param name="client_id"></param>
        /// <param name="cert"></param>
        /// <returns></returns>
        public static async Task <int> HandshakeAsync(RtmpServer server, Socket client_socket, ushort client_id, X509Certificate2 cert = null)
        {
            Stream stream;

            if (cert != null)
            {
                var temp_stream = new SslStream(new NetworkStream(client_socket));
                try
                {
                    await temp_stream.AuthenticateAsServerAsync(cert);
                }
                catch (AuthenticationException)
                {
                    temp_stream.Close();
                    throw;
                }
                stream = temp_stream;
            }
            else
            {
                stream = new NetworkStream(client_socket);
            }

            var random = new Random(Environment.TickCount);

            var randomBytes = new byte[1528];

            random.NextBytes(randomBytes);
            client_socket.NoDelay = true;

            CancellationTokenSource cts = new CancellationTokenSource();

            //over time cancel task
            Timer timer = new Timer((s) =>
            {
                cts.Cancel();
                throw new TimeoutException("rtmp 握手已超时!");
            }, null, ReceiveTimeout, Timeout.Infinite);


            //read c0 c1
            var c01 = await RtmpHandshake.ReadAsync(stream, true, cts.Token);

            timer.Change(Timeout.Infinite, Timeout.Infinite);

            //write s0 s1
            var s01 = new RtmpHandshake()
            {
                Version = 3,
                Time    = (uint)Environment.TickCount,
                Time2   = 0,
                Random  = randomBytes
            };

            timer.Change(ReceiveTimeout, Timeout.Infinite);
            await RtmpHandshake.WriteAsync(stream, s01, true, cts.Token);

            //write s2
            random.NextBytes(randomBytes);
            await C2S2Async(stream, randomBytes, cts, timer);

            timer.Change(Timeout.Infinite, Timeout.Infinite);


            // handshake check
            //if (!c0.Random.SequenceEqual(s2.Random))
            //throw new ProtocolViolationException();

            var connect = new RtmpConnect(client_socket, stream, server, client_id, server.Context, server.AmfEncoding, true);

            connect.ChannelDataReceived += server.SendDataHandler;

            server.ClientSessions.Add(client_id, new ClientSession()
            {
                Connect    = connect,
                LastPing   = DateTime.UtcNow,
                ReaderTask = null,
                WriterTask = null
            });

            return(client_id);
        }
Пример #15
0
        public async Task SslStream_StreamToStream_DuplicateOptions_Throws()
        {
            RemoteCertificateValidationCallback rCallback = (sender, certificate, chain, errors) => { return(true); };
            LocalCertificateSelectionCallback   lCallback = (sender, host, localCertificates, remoteCertificate, issuers) => { return(null); };

            VirtualNetwork network = new VirtualNetwork();

            using (var clientStream = new VirtualNetworkStream(network, false))
                using (var serverStream = new VirtualNetworkStream(network, true))
                    using (var client = new SslStream(clientStream, false, rCallback, lCallback, EncryptionPolicy.RequireEncryption))
                        using (var server = new SslStream(serverStream, false, rCallback))
                            using (X509Certificate2 certificate = Configuration.Certificates.GetServerCertificate())
                            {
                                SslClientAuthenticationOptions clientOptions = new SslClientAuthenticationOptions();
                                clientOptions.RemoteCertificateValidationCallback = AllowAnyServerCertificate;
                                clientOptions.TargetHost = certificate.GetNameInfo(X509NameType.SimpleName, false);

                                SslServerAuthenticationOptions serverOptions = new SslServerAuthenticationOptions();
                                serverOptions.ServerCertificate = certificate;
                                serverOptions.RemoteCertificateValidationCallback = AllowAnyServerCertificate;

                                Task t1 = Assert.ThrowsAsync <InvalidOperationException>(() => client.AuthenticateAsClientAsync(clientOptions, CancellationToken.None));
                                Task t2 = Assert.ThrowsAsync <InvalidOperationException>(() => server.AuthenticateAsServerAsync(serverOptions, CancellationToken.None));

                                await TestConfiguration.WhenAllOrAnyFailedWithTimeout(t1, t2);
                            }
        }
Пример #16
0
        private async Task ServerAsyncSslHelper(
            SslProtocols clientSslProtocols,
            SslProtocols serverSslProtocols,
            bool expectedToFail = false)
        {
            _log.WriteLine(
                "Server: " + serverSslProtocols + "; Client: " + clientSslProtocols +
                " expectedToFail: " + expectedToFail);

            int timeOut = expectedToFail ? TestConfiguration.FailingTestTimeoutMiliseconds
                : TestConfiguration.PassingTestTimeoutMilliseconds;

            IPEndPoint endPoint = new IPEndPoint(IPAddress.IPv6Loopback, 0);
            var        server   = new TcpListener(endPoint);

            server.Start();

            using (var clientConnection = new TcpClient(AddressFamily.InterNetworkV6))
            {
                IPEndPoint serverEndPoint = (IPEndPoint)server.LocalEndpoint;

                Task             clientConnect = clientConnection.ConnectAsync(serverEndPoint.Address, serverEndPoint.Port);
                Task <TcpClient> serverAccept  = server.AcceptTcpClientAsync();

                // We expect that the network-level connect will always complete.
                await Task.WhenAll(new Task[] { clientConnect, serverAccept }).TimeoutAfter(
                    TestConfiguration.PassingTestTimeoutMilliseconds);

                using (TcpClient serverConnection = await serverAccept)
                    using (SslStream sslClientStream = new SslStream(clientConnection.GetStream()))
                        using (SslStream sslServerStream = new SslStream(
                                   serverConnection.GetStream(),
                                   false,
                                   AllowAnyServerCertificate))
                        {
                            string serverName = _serverCertificate.GetNameInfo(X509NameType.SimpleName, false);

                            _logVerbose.WriteLine("ServerAsyncAuthenticateTest.AuthenticateAsClientAsync start.");
                            Task clientAuthentication = sslClientStream.AuthenticateAsClientAsync(
                                serverName,
                                null,
                                clientSslProtocols,
                                false);

                            _logVerbose.WriteLine("ServerAsyncAuthenticateTest.AuthenticateAsServerAsync start.");
                            Task serverAuthentication = sslServerStream.AuthenticateAsServerAsync(
                                _serverCertificate,
                                true,
                                serverSslProtocols,
                                false);

                            try
                            {
                                await clientAuthentication.TimeoutAfter(timeOut);

                                _logVerbose.WriteLine("ServerAsyncAuthenticateTest.clientAuthentication complete.");
                            }
                            catch (Exception ex)
                            {
                                // Ignore client-side errors: we're only interested in server-side behavior.
                                _log.WriteLine("Client exception: " + ex);
                            }

                            await serverAuthentication.TimeoutAfter(timeOut);

                            _logVerbose.WriteLine("ServerAsyncAuthenticateTest.serverAuthentication complete.");

                            _log.WriteLine(
                                "Server({0}) authenticated with encryption cipher: {1} {2}-bit strength",
                                serverEndPoint,
                                sslServerStream.CipherAlgorithm,
                                sslServerStream.CipherStrength);

                            Assert.True(
                                sslServerStream.CipherAlgorithm != CipherAlgorithmType.Null,
                                "Cipher algorithm should not be NULL");

                            Assert.True(sslServerStream.CipherStrength > 0, "Cipher strength should be greater than 0");
                        }
            }
        }
Пример #17
0
        public async Task ClientOptions_ServerOptions_NotMutatedDuringAuthentication()
        {
            using (X509Certificate2 clientCert = Configuration.Certificates.GetClientCertificate())
                using (X509Certificate2 serverCert = Configuration.Certificates.GetServerCertificate())
                {
                    // Values used to populate client options
                    bool clientAllowRenegotiation = false;
                    List <SslApplicationProtocol> clientAppProtocols = new List <SslApplicationProtocol> {
                        SslApplicationProtocol.Http11
                    };
                    X509RevocationMode        clientRevocation   = X509RevocationMode.NoCheck;
                    X509CertificateCollection clientCertificates = new X509CertificateCollection()
                    {
                        clientCert
                    };
                    SslProtocols     clientSslProtocols = SslProtocols.Tls12;
                    EncryptionPolicy clientEncryption   = EncryptionPolicy.RequireEncryption;
                    LocalCertificateSelectionCallback   clientLocalCallback  = new LocalCertificateSelectionCallback(delegate { return(null); });
                    RemoteCertificateValidationCallback clientRemoteCallback = new RemoteCertificateValidationCallback(delegate { return(true); });
                    string clientHost = serverCert.GetNameInfo(X509NameType.SimpleName, false);

                    // Values used to populate server options
                    bool serverAllowRenegotiation = true;
                    List <SslApplicationProtocol> serverAppProtocols = new List <SslApplicationProtocol> {
                        SslApplicationProtocol.Http11, SslApplicationProtocol.Http2
                    };
                    X509RevocationMode serverRevocation = X509RevocationMode.NoCheck;
                    bool             serverCertRequired = false;
                    SslProtocols     serverSslProtocols = SslProtocols.Tls11 | SslProtocols.Tls12;
                    EncryptionPolicy serverEncryption   = EncryptionPolicy.AllowNoEncryption;
                    RemoteCertificateValidationCallback serverRemoteCallback = new RemoteCertificateValidationCallback(delegate { return(true); });

                    var network = new VirtualNetwork();
                    using (var client = new SslStream(new VirtualNetworkStream(network, isServer: false)))
                        using (var server = new SslStream(new VirtualNetworkStream(network, isServer: true)))
                        {
                            // Create client options
                            var clientOptions = new SslClientAuthenticationOptions
                            {
                                AllowRenegotiation             = clientAllowRenegotiation,
                                ApplicationProtocols           = clientAppProtocols,
                                CertificateRevocationCheckMode = clientRevocation,
                                ClientCertificates             = clientCertificates,
                                EnabledSslProtocols            = clientSslProtocols,
                                EncryptionPolicy = clientEncryption,
                                LocalCertificateSelectionCallback   = clientLocalCallback,
                                RemoteCertificateValidationCallback = clientRemoteCallback,
                                TargetHost = clientHost
                            };

                            // Create server options
                            var serverOptions = new SslServerAuthenticationOptions
                            {
                                AllowRenegotiation             = serverAllowRenegotiation,
                                ApplicationProtocols           = serverAppProtocols,
                                CertificateRevocationCheckMode = serverRevocation,
                                ClientCertificateRequired      = serverCertRequired,
                                EnabledSslProtocols            = serverSslProtocols,
                                EncryptionPolicy = serverEncryption,
                                RemoteCertificateValidationCallback = serverRemoteCallback,
                                ServerCertificate = serverCert
                            };

                            // Authenticate
                            Task clientTask = client.AuthenticateAsClientAsync(TestAuthenticateAsync, clientOptions);
                            Task serverTask = server.AuthenticateAsServerAsync(TestAuthenticateAsync, serverOptions);
                            await new[] { clientTask, serverTask }.WhenAllOrAnyFailed();

                            // Validate that client options are unchanged
                            Assert.Equal(clientAllowRenegotiation, clientOptions.AllowRenegotiation);
                            Assert.Same(clientAppProtocols, clientOptions.ApplicationProtocols);
                            Assert.Equal(1, clientOptions.ApplicationProtocols.Count);
                            Assert.Equal(clientRevocation, clientOptions.CertificateRevocationCheckMode);
                            Assert.Same(clientCertificates, clientOptions.ClientCertificates);
                            Assert.Contains(clientCert, clientOptions.ClientCertificates.Cast <X509Certificate2>());
                            Assert.Equal(clientSslProtocols, clientOptions.EnabledSslProtocols);
                            Assert.Equal(clientEncryption, clientOptions.EncryptionPolicy);
                            Assert.Same(clientLocalCallback, clientOptions.LocalCertificateSelectionCallback);
                            Assert.Same(clientRemoteCallback, clientOptions.RemoteCertificateValidationCallback);
                            Assert.Same(clientHost, clientOptions.TargetHost);

                            // Validate that server options are unchanged
                            Assert.Equal(serverAllowRenegotiation, serverOptions.AllowRenegotiation);
                            Assert.Same(serverAppProtocols, serverOptions.ApplicationProtocols);
                            Assert.Equal(2, serverOptions.ApplicationProtocols.Count);
                            Assert.Equal(clientRevocation, serverOptions.CertificateRevocationCheckMode);
                            Assert.Equal(serverCertRequired, serverOptions.ClientCertificateRequired);
                            Assert.Equal(serverSslProtocols, serverOptions.EnabledSslProtocols);
                            Assert.Equal(serverEncryption, serverOptions.EncryptionPolicy);
                            Assert.Same(serverRemoteCallback, serverOptions.RemoteCertificateValidationCallback);
                            Assert.Same(serverCert, serverOptions.ServerCertificate);
                        }
                }
        }
Пример #18
0
 public Task Authenticate()
 {
     return(_sslStream.AuthenticateAsServerAsync(_cert, false, SslProtocols.Tls12, false));
 }
Пример #19
0
        static async Task <Tuple <EmbeddedChannel, SslStream> > SetupStreamAndChannelAsync(bool isClient, IEventExecutor executor, IWriteStrategy writeStrategy, SslProtocols protocol, List <Task> writeTasks, string targetHost)
        {
            IChannelHandler tlsHandler = isClient ?
                                         (IChannelHandler) new TlsHandler(stream => new SslStream(stream, true, (sender, certificate, chain, errors) =>
            {
                Assert.Equal(targetHost, certificate.Issuer.Replace("CN=", string.Empty));
                return(true);
            }), new ClientTlsSettings(protocol, false, new List <X509Certificate>(), targetHost)) :
                                         new SniHandler(stream => new SslStream(stream, true, (sender, certificate, chain, errors) => true), new ServerTlsSniSettings(CertificateSelector));
            //var ch = new EmbeddedChannel(new LoggingHandler("BEFORE"), tlsHandler, new LoggingHandler("AFTER"));
            var ch = new EmbeddedChannel(tlsHandler);

            if (!isClient)
            {
                // check if in the beginning snihandler exists in the pipeline, but not tls handler
                Assert.NotNull(ch.Pipeline.Get <SniHandler>());
                Assert.Null(ch.Pipeline.Get <TlsHandler>());
            }

            IByteBuffer readResultBuffer = Unpooled.Buffer(4 * 1024);
            Func <ArraySegment <byte>, Task <int> > readDataFunc = async output =>
            {
                if (writeTasks.Count > 0)
                {
                    await Task.WhenAll(writeTasks).WithTimeout(TestTimeout);

                    writeTasks.Clear();
                }

                if (readResultBuffer.ReadableBytes < output.Count)
                {
                    if (ch.Active)
                    {
                        await ReadOutboundAsync(async() => ch.ReadOutbound <IByteBuffer>(), output.Count - readResultBuffer.ReadableBytes, readResultBuffer, TestTimeout, readResultBuffer.ReadableBytes != 0? 0 : 1);
                    }
                }
                int read = Math.Min(output.Count, readResultBuffer.ReadableBytes);
                readResultBuffer.ReadBytes(output.Array, output.Offset, read);
                return(read);
            };
            var mediationStream = new MediationStream(readDataFunc, input =>
            {
                Task task = executor.SubmitAsync(() => writeStrategy.WriteToChannelAsync(ch, input)).Unwrap();
                writeTasks.Add(task);
                return(task);
            });

            var driverStream = new SslStream(mediationStream, true, (_1, _2, _3, _4) => true);

            if (isClient)
            {
                ServerTlsSettings serverTlsSettings = CertificateSelector(targetHost).Result;
                await Task.Run(() => driverStream.AuthenticateAsServerAsync(serverTlsSettings.Certificate, false, protocol | serverTlsSettings.EnabledProtocols, false).WithTimeout(TimeSpan.FromSeconds(5)));
            }
            else
            {
                await Task.Run(() => driverStream.AuthenticateAsClientAsync(targetHost, null, protocol, false)).WithTimeout(TimeSpan.FromSeconds(5));
            }
            writeTasks.Clear();

            return(Tuple.Create(ch, driverStream));
        }
Пример #20
0
        [PlatformSpecific(TestPlatforms.Linux)] // This only applies where OpenSsl is used.
        public async Task SslStream_SendReceiveOverNetworkStream_AuthenticationException()
        {
            TcpListener listener = new TcpListener(IPAddress.Loopback, 0);

            using (X509Certificate2 serverCertificate = Configuration.Certificates.GetServerCertificate())
                using (TcpClient client = new TcpClient())
                {
                    listener.Start();

                    Task             clientConnectTask  = client.ConnectAsync(IPAddress.Loopback, ((IPEndPoint)listener.LocalEndpoint).Port);
                    Task <TcpClient> listenerAcceptTask = listener.AcceptTcpClientAsync();

                    await TestConfiguration.WhenAllOrAnyFailedWithTimeout(clientConnectTask, listenerAcceptTask);

                    TcpClient server = listenerAcceptTask.Result;
                    using (SslStream clientStream = new SslStream(
                               client.GetStream(),
                               false,
                               new RemoteCertificateValidationCallback(ValidateServerCertificate),
                               null,
                               EncryptionPolicy.RequireEncryption))
                        using (SslStream serverStream = new SslStream(
                                   server.GetStream(),
                                   false,
                                   null,
                                   null,
                                   EncryptionPolicy.RequireEncryption))
                        {
                            Task clientAuthenticationTask = clientStream.AuthenticateAsClientAsync(
                                serverCertificate.GetNameInfo(X509NameType.SimpleName, false),
                                null,
                                SslProtocols.Tls11,
                                false);

                            AuthenticationException e = await Assert.ThrowsAsync <AuthenticationException>(() => serverStream.AuthenticateAsServerAsync(
                                                                                                               serverCertificate,
                                                                                                               false,
                                                                                                               SslProtocols.Tls12,
                                                                                                               false));

                            Assert.NotNull(e.InnerException);
                            Assert.True(e.InnerException.Message.Contains("SSL_ERROR_SSL"));
                            Assert.NotNull(e.InnerException.InnerException);
                            Assert.True(e.InnerException.InnerException.Message.Contains("protocol"));
                        }
                }

            listener.Stop();
        }
Пример #21
0
        private async void AcceptTcpClientCallback(IAsyncResult result)
        {
            try
            {
                var listener = (TcpListener)result.AsyncState;
                var client   = listener.EndAcceptTcpClient(result);
                listener.BeginAcceptTcpClient(AcceptTcpClientCallback, listener);
                using (client)
                {
                    var clientStream = client.GetStream();
                    var content      = await new StreamReader(clientStream).ReadLineAsync();
                    // content starts with "CONNECT" means it's trying to establish an HTTPS connection
                    if (!content.StartsWith("CONNECT"))
                    {
                        return;
                    }
                    var writer = new StreamWriter(clientStream);
                    await writer.WriteLineAsync("HTTP/1.1 200 Connection established");

                    await writer.WriteLineAsync($"Timestamp: {DateTime.Now}");

                    await writer.WriteLineAsync("Proxy-agent: Pixeval");

                    await writer.WriteLineAsync();

                    await writer.FlushAsync();

                    var clientSsl = new SslStream(clientStream, false);
                    // use specify certificate to establish the HTTPS connection
                    await clientSsl.AuthenticateAsServerAsync(certificate, false, SslProtocols.Tls | SslProtocols.Tls13 | SslProtocols.Tls12 | SslProtocols.Tls11, false);

                    // create an HTTP connection to the target IP
                    var serverSsl = await CreateConnection(ip);

                    // forwarding the HTTPS connection without SNI
                    var request = Task.Run(() =>
                    {
                        try
                        {
                            clientSsl.CopyTo(serverSsl);
                        }
                        catch
                        {
                            // ignore
                        }
                    });
                    var response = Task.Run(() =>
                    {
                        try
                        {
                            serverSsl.CopyTo(clientSsl);
                        }
                        catch
                        {
                            // ignore
                        }
                    });
                    Task.WaitAny(request, response);
                    serverSsl.Close();
                }
            }
            catch
            {
                // ignore
            }
        }
Пример #22
0
        async Task ExecuteRequest(TcpClient client)
        {
            // By default we will close the stream to cater for failure scenarios
            var keepStreamOpen = false;

            var clientName = client.Client.RemoteEndPoint;
            var stream     = client.GetStream();

            using (var ssl = new SslStream(stream, true, AcceptAnySslCertificate))
            {
                try
                {
                    log.Write(EventType.Security, "Performing TLS server handshake");
                    await ssl.AuthenticateAsServerAsync(serverCertificate, true, SslProtocols.Tls | SslProtocols.Tls11 | SslProtocols.Tls12, false);

                    log.Write(EventType.Security, "Secure connection established, client is not yet authenticated, client connected with {0}", ssl.SslProtocol.ToString());

                    var req = ReadInitialRequest(ssl);
                    if (string.IsNullOrEmpty(req))
                    {
                        log.Write(EventType.Diagnostic, "Ignoring empty request");
                        return;
                    }

                    if (req.Substring(0, 2) != "MX")
                    {
                        log.Write(EventType.Diagnostic, "Appears to be a web browser, sending friendly HTML response");
                        SendFriendlyHtmlPage(ssl);
                        return;
                    }

                    if (Authorize(ssl, clientName))
                    {
                        // Delegate the open stream to the protocol handler - we no longer own the stream lifetime
                        await ExchangeMessages(ssl);

                        // Mark the stream as delegated once everything has succeeded
                        keepStreamOpen = true;
                    }
                }
                catch (AuthenticationException ex)
                {
                    log.WriteException(EventType.ClientDenied, "Client failed authentication: {0}", ex, clientName);
                }
                catch (Exception ex)
                {
                    log.WriteException(EventType.Error, "Unhandled error when handling request from client: {0}", ex, clientName);
                }
                finally
                {
                    if (!keepStreamOpen)
                    {
                        // Closing an already closed stream or client is safe, better not to leak
#if NET40
                        stream.Close();
                        client.Close();
#else
                        stream.Dispose();
                        client.Dispose();
#endif
                    }
                }
            }
        }
        public async Task CertificateValidationClientServer_EndToEnd_Ok(bool useClientSelectionCallback)
        {
            IPEndPoint endPoint = new IPEndPoint(IPAddress.IPv6Loopback, 0);
            var server = new TcpListener(endPoint);
            server.Start();

            using (var clientConnection = new TcpClient(AddressFamily.InterNetworkV6))
            {
                IPEndPoint serverEndPoint = (IPEndPoint)server.LocalEndpoint;

                Task clientConnect = clientConnection.ConnectAsync(serverEndPoint.Address, serverEndPoint.Port);
                Task<TcpClient> serverAccept = server.AcceptTcpClientAsync();

                Assert.True(
                    Task.WaitAll(
                        new Task[] { clientConnect, serverAccept },
                        TestConfiguration.PassingTestTimeoutMilliseconds),
                    "Client/Server TCP Connect timed out.");

                LocalCertificateSelectionCallback clientCertCallback = null;

                if (useClientSelectionCallback)
                {
                    clientCertCallback = ClientCertSelectionCallback;
                }

                using (TcpClient serverConnection = await serverAccept)
                using (SslStream sslClientStream = new SslStream(
                    clientConnection.GetStream(),
                    false,
                    ClientSideRemoteServerCertificateValidation,
                    clientCertCallback))
                using (SslStream sslServerStream = new SslStream(
                    serverConnection.GetStream(),
                    false,
                    ServerSideRemoteClientCertificateValidation))

                {
                    string serverName = _serverCertificate.GetNameInfo(X509NameType.SimpleName, false);
                    var clientCerts = new X509CertificateCollection();

                    if (!useClientSelectionCallback)
                    {
                        clientCerts.Add(_clientCertificate);
                    }

                    Task clientAuthentication = sslClientStream.AuthenticateAsClientAsync(
                        serverName,
                        clientCerts,
                        SslProtocolSupport.DefaultSslProtocols,
                        false);

                    Task serverAuthentication = sslServerStream.AuthenticateAsServerAsync(
                        _serverCertificate,
                        true,
                        SslProtocolSupport.DefaultSslProtocols,
                        false);

                    Assert.True(
                        Task.WaitAll(
                            new Task[] { clientAuthentication, serverAuthentication },
                            TestConfiguration.PassingTestTimeoutMilliseconds),
                        "Client/Server Authentication timed out.");

                    Assert.True(sslClientStream.IsMutuallyAuthenticated, "sslClientStream.IsMutuallyAuthenticated");
                    Assert.True(sslServerStream.IsMutuallyAuthenticated, "sslServerStream.IsMutuallyAuthenticated");

                    Assert.Equal(sslClientStream.RemoteCertificate.Subject, _serverCertificate.Subject);
                    Assert.Equal(sslServerStream.RemoteCertificate.Subject, _clientCertificate.Subject);
                }
            }
        }
        public async Task SslStream_UntrustedCaWithCustomCallback_OK(bool usePartialChain)
        {
            var rnd   = new Random();
            int split = rnd.Next(0, _serverChain.Count - 1);

            var clientOptions = new  SslClientAuthenticationOptions()
            {
                TargetHost = "localhost"
            };

            clientOptions.RemoteCertificateValidationCallback =
                (sender, certificate, chain, sslPolicyErrors) =>
            {
                // add our custom root CA
                chain.ChainPolicy.CustomTrustStore.Add(_serverChain[_serverChain.Count - 1]);
                chain.ChainPolicy.TrustMode = X509ChainTrustMode.CustomRootTrust;
                // Add only one CA to verify that peer did send intermediate CA cert.
                // In case of partial chain, we need to make missing certs available.
                if (usePartialChain)
                {
                    for (int i = split; i < _serverChain.Count - 1; i++)
                    {
                        chain.ChainPolicy.ExtraStore.Add(_serverChain[i]);
                    }
                }

                bool result = chain.Build((X509Certificate2)certificate);
                Assert.True(result);

                return(result);
            };

            var serverOptions = new SslServerAuthenticationOptions();
            X509Certificate2Collection serverChain;

            if (usePartialChain)
            {
                // give first few certificates without root CA
                serverChain = new X509Certificate2Collection();
                for (int i = 0; i < split; i++)
                {
                    serverChain.Add(_serverChain[i]);
                }
            }
            else
            {
                serverChain = _serverChain;
            }

            serverOptions.ServerCertificateContext = SslStreamCertificateContext.Create(_serverCert, serverChain);

            (Stream clientStream, Stream serverStream) = TestHelper.GetConnectedStreams();
            using (clientStream)
                using (serverStream)
                    using (SslStream client = new SslStream(clientStream))
                        using (SslStream server = new SslStream(serverStream))
                        {
                            Task t1 = client.AuthenticateAsClientAsync(clientOptions, CancellationToken.None);
                            Task t2 = server.AuthenticateAsServerAsync(serverOptions, CancellationToken.None);

                            await TestConfiguration.WhenAllOrAnyFailedWithTimeout(t1, t2);
                        }
        }
Пример #25
0
        public async Task RunTest()
        {
            bool done = false;

            while (!done)
            {
                try
                {
                    using (TcpClient requestClient = await _listener.AcceptTcpClientAsync())
                    {
                        _log.WriteLine("[Server] Client connected.");

                        using (var tls = new SslStream(requestClient.GetStream()))
                        {
                            await tls.AuthenticateAsServerAsync(
                                _serverCertificate,
                                false,
                                SslProtocols.Tls,
                                false);

                            _log.WriteLine("[Server] Client authenticated.");

                            done = await HttpConversation(tls);
                        }
                    }
                }
                catch (IOException)
                {
                    // Ignore I/O issues as browsers attempt to connect only to detect crypto information.
                }
            }

            _listener.Stop();
        }
Пример #26
0
 public Task AuthAsServer(System.Security.Cryptography.X509Certificates.X509Certificate certificate)
 {
     return(SslStream.AuthenticateAsServerAsync(certificate));
 }
Пример #27
0
        public async Task SslStream_StreamToStream_Alpn_NonMatchingProtocols_Fail()
        {
            TcpListener listener = new TcpListener(IPAddress.Loopback, 0);

            try
            {
                listener.Start();
                using (TcpClient client = new TcpClient())
                {
                    Task <TcpClient> serverTask = listener.AcceptTcpClientAsync();
                    await client.ConnectAsync(IPAddress.Loopback, ((IPEndPoint)listener.LocalEndpoint).Port);

                    using (TcpClient server = await serverTask)
                        using (SslStream serverStream = new SslStream(server.GetStream(), leaveInnerStreamOpen: false))
                            using (SslStream clientStream = new SslStream(client.GetStream(), leaveInnerStreamOpen: false))
                                using (X509Certificate2 certificate = Configuration.Certificates.GetServerCertificate())
                                {
                                    SslServerAuthenticationOptions serverOptions = new SslServerAuthenticationOptions
                                    {
                                        ApplicationProtocols = new List <SslApplicationProtocol> {
                                            SslApplicationProtocol.Http2
                                        },
                                        ServerCertificate = certificate,
                                    };
                                    SslClientAuthenticationOptions clientOptions = new SslClientAuthenticationOptions
                                    {
                                        ApplicationProtocols = new List <SslApplicationProtocol> {
                                            SslApplicationProtocol.Http11
                                        },
                                        RemoteCertificateValidationCallback = AllowAnyServerCertificate,
                                        TargetHost = certificate.GetNameInfo(X509NameType.SimpleName, false),
                                    };

                                    // Test alpn failure only on platforms that supports ALPN.
                                    if (BackendSupportsAlpn)
                                    {
                                        Task t1 = Assert.ThrowsAsync <IOException>(() => clientStream.AuthenticateAsClientAsync(clientOptions, CancellationToken.None));
                                        try
                                        {
                                            await serverStream.AuthenticateAsServerAsync(serverOptions, CancellationToken.None);

                                            Assert.True(false, "AuthenticationException was not thrown.");
                                        }
                                        catch (AuthenticationException) { server.Dispose(); }

                                        await TestConfiguration.WhenAllOrAnyFailedWithTimeout(t1);
                                    }
                                    else
                                    {
                                        Task t1 = clientStream.AuthenticateAsClientAsync(clientOptions, CancellationToken.None);
                                        Task t2 = serverStream.AuthenticateAsServerAsync(serverOptions, CancellationToken.None);

                                        await TestConfiguration.WhenAllOrAnyFailedWithTimeout(t1, t2);

                                        Assert.Equal(default(SslApplicationProtocol), clientStream.NegotiatedApplicationProtocol);
                                        Assert.Equal(default(SslApplicationProtocol), serverStream.NegotiatedApplicationProtocol);
                                    }
                                }
                }
            }
            finally
            {
                listener.Stop();
            }
        }
Пример #28
0
        /// <summary>
        /// This is called when client is aware of proxy
        /// So for HTTPS requests client would send CONNECT header to negotiate a secure tcp tunnel via proxy
        /// </summary>
        /// <param name="endPoint"></param>
        /// <param name="tcpClient"></param>
        /// <returns></returns>
        private async Task HandleClient(ExplicitProxyEndPoint endPoint, TcpClient tcpClient)
        {
            bool disposed = false;

            var clientStream = new CustomBufferedStream(tcpClient.GetStream(), BufferSize);

            var clientStreamReader = new CustomBinaryReader(clientStream, BufferSize);
            var clientStreamWriter = new HttpResponseWriter(clientStream, BufferSize);

            Uri httpRemoteUri;

            try
            {
                //read the first line HTTP command
                string httpCmd = await clientStreamReader.ReadLineAsync();

                if (string.IsNullOrEmpty(httpCmd))
                {
                    return;
                }

                string  httpMethod;
                string  httpUrl;
                Version version;
                Request.ParseRequestLine(httpCmd, out httpMethod, out httpUrl, out version);

                httpRemoteUri = httpMethod == "CONNECT" ? new Uri("http://" + httpUrl) : new Uri(httpUrl);

                //filter out excluded host names
                bool excluded = false;

                if (endPoint.ExcludedHttpsHostNameRegex != null)
                {
                    excluded = endPoint.ExcludedHttpsHostNameRegexList.Any(x => x.IsMatch(httpRemoteUri.Host));
                }

                if (endPoint.IncludedHttpsHostNameRegex != null)
                {
                    excluded = !endPoint.IncludedHttpsHostNameRegexList.Any(x => x.IsMatch(httpRemoteUri.Host));
                }

                ConnectRequest connectRequest = null;

                //Client wants to create a secure tcp tunnel (probably its a HTTPS or Websocket request)
                if (httpMethod == "CONNECT")
                {
                    connectRequest = new ConnectRequest
                    {
                        RequestUri         = httpRemoteUri,
                        OriginalRequestUrl = httpUrl,
                        HttpVersion        = version,
                        Method             = httpMethod,
                    };

                    await HeaderParser.ReadHeaders(clientStreamReader, connectRequest.RequestHeaders);

                    var connectArgs = new TunnelConnectSessionEventArgs(BufferSize, endPoint);
                    connectArgs.WebSession.Request       = connectRequest;
                    connectArgs.ProxyClient.TcpClient    = tcpClient;
                    connectArgs.ProxyClient.ClientStream = clientStream;

                    if (TunnelConnectRequest != null)
                    {
                        await TunnelConnectRequest.InvokeParallelAsync(this, connectArgs, ExceptionFunc);
                    }

                    if (!excluded && await CheckAuthorization(clientStreamWriter, connectArgs) == false)
                    {
                        if (TunnelConnectResponse != null)
                        {
                            await TunnelConnectResponse.InvokeParallelAsync(this, connectArgs, ExceptionFunc);
                        }

                        return;
                    }

                    //write back successfull CONNECT response
                    connectArgs.WebSession.Response = ConnectResponse.CreateSuccessfullConnectResponse(version);
                    await clientStreamWriter.WriteResponseAsync(connectArgs.WebSession.Response);

                    var clientHelloInfo = await SslTools.PeekClientHello(clientStream);

                    bool isClientHello = clientHelloInfo != null;
                    if (isClientHello)
                    {
                        connectRequest.ClientHelloInfo = clientHelloInfo;
                    }

                    if (TunnelConnectResponse != null)
                    {
                        connectArgs.IsHttpsConnect = isClientHello;
                        await TunnelConnectResponse.InvokeParallelAsync(this, connectArgs, ExceptionFunc);
                    }

                    if (!excluded && isClientHello)
                    {
                        httpRemoteUri             = new Uri("https://" + httpUrl);
                        connectRequest.RequestUri = httpRemoteUri;

                        SslStream sslStream = null;

                        try
                        {
                            sslStream = new SslStream(clientStream);

                            string certName = HttpHelper.GetWildCardDomainName(httpRemoteUri.Host);

                            var certificate = endPoint.GenericCertificate ?? CertificateManager.CreateCertificate(certName, false);

                            //Successfully managed to authenticate the client using the fake certificate
                            await sslStream.AuthenticateAsServerAsync(certificate, false, SupportedSslProtocols, false);

                            //HTTPS server created - we can now decrypt the client's traffic
                            clientStream = new CustomBufferedStream(sslStream, BufferSize);

                            clientStreamReader.Dispose();
                            clientStreamReader = new CustomBinaryReader(clientStream, BufferSize);
                            clientStreamWriter = new HttpResponseWriter(clientStream, BufferSize);
                        }
                        catch
                        {
                            sslStream?.Dispose();
                            return;
                        }

                        //Now read the actual HTTPS request line
                        httpCmd = await clientStreamReader.ReadLineAsync();
                    }
                    //Hostname is excluded or it is not an HTTPS connect
                    else
                    {
                        //create new connection
                        using (var connection = await GetServerConnection(connectArgs, true))
                        {
                            try
                            {
                                if (isClientHello)
                                {
                                    if (clientStream.Available > 0)
                                    {
                                        //send the buffered data
                                        var data = new byte[clientStream.Available];
                                        await clientStream.ReadAsync(data, 0, data.Length);

                                        await connection.Stream.WriteAsync(data, 0, data.Length);

                                        await connection.Stream.FlushAsync();
                                    }

                                    var serverHelloInfo = await SslTools.PeekServerHello(connection.Stream);

                                    ((ConnectResponse)connectArgs.WebSession.Response).ServerHelloInfo = serverHelloInfo;
                                }

                                await TcpHelper.SendRaw(clientStream, connection.Stream, BufferSize,
                                                        (buffer, offset, count) => { connectArgs.OnDataSent(buffer, offset, count); }, (buffer, offset, count) => { connectArgs.OnDataReceived(buffer, offset, count); });
                            }
                            finally
                            {
                                UpdateServerConnectionCount(false);
                            }
                        }

                        return;
                    }
                }

                //Now create the request
                disposed = await HandleHttpSessionRequest(tcpClient, httpCmd, clientStream, clientStreamReader, clientStreamWriter,
                                                          httpRemoteUri.Scheme == UriSchemeHttps?httpRemoteUri.Host : null, endPoint, connectRequest);
            }
            catch (Exception e)
            {
                ExceptionFunc(new Exception("Error whilst authorizing request", e));
            }
            finally
            {
                if (!disposed)
                {
                    Dispose(clientStream, clientStreamReader, clientStreamWriter, null);
                }
            }
        }
Пример #29
0
        public async Task SslStream_ServerCallbackNotSet_UsesLocalCertificateSelection(string hostName)
        {
            X509Certificate serverCert = Configuration.Certificates.GetSelfSignedServerCertificate();

            int timesCallbackCalled = 0;

            var selectionCallback = new LocalCertificateSelectionCallback((object sender, string targetHost, X509CertificateCollection localCertificates, X509Certificate remoteCertificate, string[] issuers) =>
            {
                Assert.Equal(string.Empty, targetHost);
                Assert.True(localCertificates.Contains(serverCert));
                timesCallbackCalled++;
                return(serverCert);
            });

            var validationCallback = new RemoteCertificateValidationCallback((object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors) =>
            {
                Assert.Equal(serverCert, certificate);
                return(true);
            });

            VirtualNetwork vn = new VirtualNetwork();

            using (VirtualNetworkStream serverStream = new VirtualNetworkStream(vn, isServer: true),
                   clientStream = new VirtualNetworkStream(vn, isServer: false))
                using (SslStream server = new SslStream(serverStream, false, null, selectionCallback),
                       client = new SslStream(clientStream, leaveInnerStreamOpen: false, validationCallback))
                {
                    Task clientJob = Task.Run(() => {
                        client.AuthenticateAsClient(hostName);
                    });

                    SslServerAuthenticationOptions options = DefaultServerOptions();
                    options.ServerCertificate = serverCert;

                    await TaskTimeoutExtensions.WhenAllOrAnyFailed(new[] { clientJob, server.AuthenticateAsServerAsync(options, CancellationToken.None) });

                    Assert.Equal(1, timesCallbackCalled);
                }
        }
Пример #30
0
 internal static Task AuthenticateAsServerAsync(this SslStream sslStream, SslServerAuthenticationOptions options,
                                                CancellationToken token)
 {
     return(sslStream.AuthenticateAsServerAsync(options.ServerCertificate, options.ClientCertificateRequired,
                                                options.EnabledSslProtocols, options.CertificateRevocationCheckMode != X509RevocationMode.NoCheck));
 }
        /// <summary>
        /// This is called when client is aware of proxy
        /// So for HTTPS requests client would send CONNECT header to negotiate a secure tcp tunnel via proxy
        /// </summary>
        /// <param name="endPoint"></param>
        /// <param name="tcpClient"></param>
        /// <returns></returns>
        private async Task HandleClient(ExplicitProxyEndPoint endPoint, TcpClient tcpClient)
        {
            var disposed = false;

            var clientStream = new CustomBufferedStream(tcpClient.GetStream(), BufferSize);

            var clientStreamReader = new CustomBinaryReader(clientStream, BufferSize);
            var clientStreamWriter = new StreamWriter(clientStream)
            {
                NewLine = ProxyConstants.NewLine
            };

            Uri httpRemoteUri;

            try
            {
                //read the first line HTTP command
                var httpCmd = await clientStreamReader.ReadLineAsync();

                if (string.IsNullOrEmpty(httpCmd))
                {
                    return;
                }

                //break up the line into three components (method, remote URL & Http Version)
                var httpCmdSplit = httpCmd.Split(ProxyConstants.SpaceSplit, 3);

                //Find the request Verb
                var httpVerb = httpCmdSplit[0].ToUpper();

                httpRemoteUri = httpVerb == "CONNECT" ? new Uri("http://" + httpCmdSplit[1]) : new Uri(httpCmdSplit[1]);

                //parse the HTTP version
                var version = HttpHeader.Version11;
                if (httpCmdSplit.Length == 3)
                {
                    var httpVersion = httpCmdSplit[2].Trim();

                    if (string.Equals(httpVersion, "HTTP/1.0", StringComparison.OrdinalIgnoreCase))
                    {
                        version = HttpHeader.Version10;
                    }
                }

                //filter out excluded host names
                bool excluded = false;

                if (endPoint.ExcludedHttpsHostNameRegex != null)
                {
                    excluded = endPoint.ExcludedHttpsHostNameRegexList.Any(x => x.IsMatch(httpRemoteUri.Host));
                }

                if (endPoint.IncludedHttpsHostNameRegex != null)
                {
                    excluded = !endPoint.IncludedHttpsHostNameRegexList.Any(x => x.IsMatch(httpRemoteUri.Host));
                }

                List <HttpHeader> connectRequestHeaders = null;

                //Client wants to create a secure tcp tunnel (its a HTTPS request)
                if (httpVerb == "CONNECT" && !excluded &&
                    endPoint.RemoteHttpsPorts.Contains(httpRemoteUri.Port))
                {
                    httpRemoteUri         = new Uri("https://" + httpCmdSplit[1]);
                    connectRequestHeaders = new List <HttpHeader>();
                    string tmpLine;
                    while (!string.IsNullOrEmpty(tmpLine = await clientStreamReader.ReadLineAsync()))
                    {
                        var header = tmpLine.Split(ProxyConstants.ColonSplit, 2);

                        var newHeader = new HttpHeader(header[0], header[1]);
                        connectRequestHeaders.Add(newHeader);
                    }

                    if (await CheckAuthorization(clientStreamWriter, connectRequestHeaders) == false)
                    {
                        return;
                    }

                    await WriteConnectResponse(clientStreamWriter, version);

                    SslStream sslStream = null;

                    try
                    {
                        sslStream = new SslStream(clientStream);

                        var certName = HttpHelper.GetWildCardDomainName(httpRemoteUri.Host);

                        var certificate = endPoint.GenericCertificate ??
                                          CertificateManager.CreateCertificate(certName, false);

                        //Successfully managed to authenticate the client using the fake certificate
                        await sslStream.AuthenticateAsServerAsync(certificate, false,
                                                                  SupportedSslProtocols, false);

                        //HTTPS server created - we can now decrypt the client's traffic
                        clientStream = new CustomBufferedStream(sslStream, BufferSize);

                        clientStreamReader.Dispose();
                        clientStreamReader = new CustomBinaryReader(clientStream, BufferSize);
                        clientStreamWriter = new StreamWriter(clientStream)
                        {
                            NewLine = ProxyConstants.NewLine
                        };
                    }
                    catch
                    {
                        sslStream?.Dispose();
                        return;
                    }

                    //Now read the actual HTTPS request line
                    httpCmd = await clientStreamReader.ReadLineAsync();
                }
                //Sorry cannot do a HTTPS request decrypt to port 80 at this time
                else if (httpVerb == "CONNECT")
                {
                    //Siphon out CONNECT request headers
                    await clientStreamReader.ReadAndIgnoreAllLinesAsync();

                    //write back successfull CONNECT response
                    await WriteConnectResponse(clientStreamWriter, version);

                    await TcpHelper.SendRaw(this,
                                            httpRemoteUri.Host, httpRemoteUri.Port,
                                            null, version, null,
                                            false,
                                            clientStream, tcpConnectionFactory);

                    return;
                }

                //Now create the request
                disposed = await HandleHttpSessionRequest(tcpClient, httpCmd, clientStream, clientStreamReader, clientStreamWriter,
                                                          httpRemoteUri.Scheme == Uri.UriSchemeHttps?httpRemoteUri.Host : null, endPoint,
                                                          connectRequestHeaders);
            }
            catch (Exception e)
            {
                ExceptionFunc(new Exception("Error whilst authorizing request", e));
            }
            finally
            {
                if (!disposed)
                {
                    Dispose(clientStream, clientStreamReader, clientStreamWriter, null);
                }
            }
        }
Пример #32
0
        /// <summary>
        ///     This is called when client is aware of proxy
        ///     So for HTTPS requests client would send CONNECT header to negotiate a secure tcp tunnel via proxy
        /// </summary>
        /// <param name="endPoint">The explicit endpoint.</param>
        /// <param name="clientConnection">The client connection.</param>
        /// <returns>The task.</returns>
        private async Task handleClient(ExplicitProxyEndPoint endPoint, TcpClientConnection clientConnection)
        {
            var cancellationTokenSource = new CancellationTokenSource();
            var cancellationToken       = cancellationTokenSource.Token;

            var clientStream = new HttpClientStream(this, clientConnection, clientConnection.GetStream(), BufferPool, cancellationToken);

            Task <TcpServerConnection?>?prefetchConnectionTask = null;
            bool closeServerConnection = false;

            TunnelConnectSessionEventArgs?connectArgs = null;

            try
            {
                var method = await HttpHelper.GetMethod(clientStream, BufferPool, cancellationToken);

                if (clientStream.IsClosed)
                {
                    return;
                }

                // Client wants to create a secure tcp tunnel (probably its a HTTPS or Websocket request)
                if (method == KnownMethod.Connect)
                {
                    // read the first line HTTP command
                    var requestLine = await clientStream.ReadRequestLine(cancellationToken);

                    if (requestLine.IsEmpty())
                    {
                        return;
                    }

                    var connectRequest = new ConnectRequest(requestLine.RequestUri)
                    {
                        RequestUriString8 = requestLine.RequestUri,
                        HttpVersion       = requestLine.Version
                    };

                    await HeaderParser.ReadHeaders(clientStream, connectRequest.Headers, cancellationToken);

                    connectArgs             = new TunnelConnectSessionEventArgs(this, endPoint, connectRequest, clientStream, cancellationTokenSource);
                    clientStream.DataRead  += (o, args) => connectArgs.OnDataSent(args.Buffer, args.Offset, args.Count);
                    clientStream.DataWrite += (o, args) => connectArgs.OnDataReceived(args.Buffer, args.Offset, args.Count);

                    await endPoint.InvokeBeforeTunnelConnectRequest(this, connectArgs, ExceptionFunc);

                    // filter out excluded host names
                    bool decryptSsl  = endPoint.DecryptSsl && connectArgs.DecryptSsl;
                    bool sendRawData = !decryptSsl;

                    if (connectArgs.DenyConnect)
                    {
                        if (connectArgs.HttpClient.Response.StatusCode == 0)
                        {
                            connectArgs.HttpClient.Response = new Response
                            {
                                HttpVersion       = HttpHeader.Version11,
                                StatusCode        = (int)HttpStatusCode.Forbidden,
                                StatusDescription = "Forbidden"
                            };
                        }

                        // send the response
                        await clientStream.WriteResponseAsync(connectArgs.HttpClient.Response, cancellationToken);

                        return;
                    }

                    if (await checkAuthorization(connectArgs) == false)
                    {
                        await endPoint.InvokeBeforeTunnelConnectResponse(this, connectArgs, ExceptionFunc);

                        // send the response
                        await clientStream.WriteResponseAsync(connectArgs.HttpClient.Response, cancellationToken);

                        return;
                    }

                    // write back successful CONNECT response
                    var response = ConnectResponse.CreateSuccessfulConnectResponse(connectRequest.HttpVersion);

                    // Set ContentLength explicitly to properly handle HTTP 1.0
                    response.ContentLength = 0;
                    response.Headers.FixProxyHeaders();
                    connectArgs.HttpClient.Response = response;

                    await clientStream.WriteResponseAsync(response, cancellationToken);

                    var clientHelloInfo = await SslTools.PeekClientHello(clientStream, BufferPool, cancellationToken);

                    if (clientStream.IsClosed)
                    {
                        return;
                    }

                    bool isClientHello = clientHelloInfo != null;
                    if (clientHelloInfo != null)
                    {
                        connectRequest.TunnelType      = TunnelType.Https;
                        connectRequest.ClientHelloInfo = clientHelloInfo;
                    }

                    await endPoint.InvokeBeforeTunnelConnectResponse(this, connectArgs, ExceptionFunc, isClientHello);

                    if (decryptSsl && clientHelloInfo != null)
                    {
                        connectRequest.IsHttps = true; // todo: move this line to the previous "if"
                        clientStream.Connection.SslProtocol = clientHelloInfo.SslProtocol;

                        bool http2Supported = false;

                        if (EnableHttp2)
                        {
                            var alpn = clientHelloInfo.GetAlpn();
                            if (alpn != null && alpn.Contains(SslApplicationProtocol.Http2))
                            {
                                // test server HTTP/2 support
                                try
                                {
                                    // todo: this is a hack, because Titanium does not support HTTP protocol changing currently
                                    var connection = await tcpConnectionFactory.GetServerConnection(this, connectArgs,
                                                                                                    true, SslExtensions.Http2ProtocolAsList,
                                                                                                    true, true, cancellationToken);

                                    if (connection != null)
                                    {
                                        http2Supported = connection.NegotiatedApplicationProtocol ==
                                                         SslApplicationProtocol.Http2;

                                        // release connection back to pool instead of closing when connection pool is enabled.
                                        await tcpConnectionFactory.Release(connection, true);
                                    }
                                }
                                catch (Exception)
                                {
                                    // ignore
                                }
                            }
                        }

                        if (EnableTcpServerConnectionPrefetch)
                        {
                            // don't pass cancellation token here
                            // it could cause floating server connections when client exits
                            prefetchConnectionTask = tcpConnectionFactory.GetServerConnection(this, connectArgs,
                                                                                              true, null, false, true,
                                                                                              CancellationToken.None);
                        }

                        string connectHostname = requestLine.RequestUri.GetString();
                        int    idx             = connectHostname.IndexOf(":");
                        if (idx >= 0)
                        {
                            connectHostname = connectHostname.Substring(0, idx);
                        }

                        X509Certificate2?certificate = null;
                        SslStream?       sslStream   = null;
                        try
                        {
                            sslStream = new SslStream(clientStream, false);

                            string certName = HttpHelper.GetWildCardDomainName(connectHostname);
                            certificate = endPoint.GenericCertificate ??
                                          await CertificateManager.CreateServerCertificate(certName);

                            // Successfully managed to authenticate the client using the fake certificate
                            var options = new SslServerAuthenticationOptions();
                            if (EnableHttp2 && http2Supported)
                            {
                                options.ApplicationProtocols = clientHelloInfo.GetAlpn();
                                if (options.ApplicationProtocols == null || options.ApplicationProtocols.Count == 0)
                                {
                                    options.ApplicationProtocols = SslExtensions.Http11ProtocolAsList;
                                }
                            }

                            options.ServerCertificate              = certificate;
                            options.ClientCertificateRequired      = false;
                            options.EnabledSslProtocols            = SupportedSslProtocols;
                            options.CertificateRevocationCheckMode = X509RevocationMode.NoCheck;
                            await sslStream.AuthenticateAsServerAsync(options, cancellationToken);

#if NETSTANDARD2_1
                            clientStream.Connection.NegotiatedApplicationProtocol = sslStream.NegotiatedApplicationProtocol;
#endif

                            // HTTPS server created - we can now decrypt the client's traffic
                            clientStream = new HttpClientStream(this, clientStream.Connection, sslStream, BufferPool, cancellationToken);
                            sslStream    = null; // clientStream was created, no need to keep SSL stream reference

                            clientStream.DataRead  += (o, args) => connectArgs.OnDecryptedDataSent(args.Buffer, args.Offset, args.Count);
                            clientStream.DataWrite += (o, args) => connectArgs.OnDecryptedDataReceived(args.Buffer, args.Offset, args.Count);
                        }
                        catch (Exception e)
                        {
                            sslStream?.Dispose();

                            var certName = certificate?.GetNameInfo(X509NameType.SimpleName, false);
                            throw new ProxyConnectException(
                                      $"Couldn't authenticate host '{connectHostname}' with certificate '{certName}'.", e, connectArgs);
                        }

                        method = await HttpHelper.GetMethod(clientStream, BufferPool, cancellationToken);

                        if (clientStream.IsClosed)
                        {
                            return;
                        }

                        if (method == KnownMethod.Invalid)
                        {
                            sendRawData = true;
                            await tcpConnectionFactory.Release(prefetchConnectionTask, true);

                            prefetchConnectionTask = null;
                        }
                    }
                    else if (clientHelloInfo == null)
                    {
                        method = await HttpHelper.GetMethod(clientStream, BufferPool, cancellationToken);

                        if (clientStream.IsClosed)
                        {
                            return;
                        }
                    }

                    if (cancellationTokenSource.IsCancellationRequested)
                    {
                        throw new Exception("Session was terminated by user.");
                    }

                    if (method == KnownMethod.Invalid)
                    {
                        sendRawData = true;
                    }

                    // Hostname is excluded or it is not an HTTPS connect
                    if (sendRawData)
                    {
                        // create new connection to server.
                        // If we detected that client tunnel CONNECTs without SSL by checking for empty client hello then
                        // this connection should not be HTTPS.
                        var connection = (await tcpConnectionFactory.GetServerConnection(this, connectArgs,
                                                                                         true, null,
                                                                                         true, false, cancellationToken)) !;

                        try
                        {
                            if (isClientHello)
                            {
                                int available = clientStream.Available;
                                if (available > 0)
                                {
                                    // send the buffered data
                                    var data = BufferPool.GetBuffer();

                                    try
                                    {
                                        // clientStream.Available should be at most BufferSize because it is using the same buffer size
                                        int read = await clientStream.ReadAsync(data, 0, available, cancellationToken);

                                        if (read != available)
                                        {
                                            throw new Exception("Internal error.");
                                        }

                                        await connection.Stream.WriteAsync(data, 0, available, true, cancellationToken);
                                    }
                                    finally
                                    {
                                        BufferPool.ReturnBuffer(data);
                                    }
                                }

                                var serverHelloInfo = await SslTools.PeekServerHello(connection.Stream, BufferPool, cancellationToken);

                                ((ConnectResponse)connectArgs.HttpClient.Response).ServerHelloInfo = serverHelloInfo;
                            }

                            if (!clientStream.IsClosed && !connection.Stream.IsClosed)
                            {
                                await TcpHelper.SendRaw(clientStream, connection.Stream, BufferPool,
                                                        null, null, connectArgs.CancellationTokenSource, ExceptionFunc);
                            }
                        }
                        finally
                        {
                            await tcpConnectionFactory.Release(connection, true);
                        }

                        return;
                    }
                }

                if (connectArgs != null && method == KnownMethod.Pri)
                {
                    // todo
                    string?httpCmd = await clientStream.ReadLineAsync(cancellationToken);

                    if (httpCmd == "PRI * HTTP/2.0")
                    {
                        connectArgs.HttpClient.ConnectRequest !.TunnelType = TunnelType.Http2;

                        // HTTP/2 Connection Preface
                        string?line = await clientStream.ReadLineAsync(cancellationToken);

                        if (line != string.Empty)
                        {
                            throw new Exception($"HTTP/2 Protocol violation. Empty string expected, '{line}' received");
                        }

                        line = await clientStream.ReadLineAsync(cancellationToken);

                        if (line != "SM")
                        {
                            throw new Exception($"HTTP/2 Protocol violation. 'SM' expected, '{line}' received");
                        }

                        line = await clientStream.ReadLineAsync(cancellationToken);

                        if (line != string.Empty)
                        {
                            throw new Exception($"HTTP/2 Protocol violation. Empty string expected, '{line}' received");
                        }

                        var connection = (await tcpConnectionFactory.GetServerConnection(this, connectArgs,
                                                                                         true, SslExtensions.Http2ProtocolAsList,
                                                                                         true, false, cancellationToken)) !;
                        try
                        {
#if NETSTANDARD2_1
                            var connectionPreface = new ReadOnlyMemory <byte>(Http2Helper.ConnectionPreface);
                            await connection.Stream.WriteAsync(connectionPreface, cancellationToken);

                            await Http2Helper.SendHttp2(clientStream, connection.Stream,
                                                        () => new SessionEventArgs(this, endPoint, clientStream, connectArgs?.HttpClient.ConnectRequest, cancellationTokenSource)
                            {
                                UserData = connectArgs?.UserData
                            },
                                                        async args => { await onBeforeRequest(args); },
                                                        async args => { await onBeforeResponse(args); },
                                                        connectArgs.CancellationTokenSource, clientStream.Connection.Id, ExceptionFunc);
#endif
                        }
                        finally
                        {
                            await tcpConnectionFactory.Release(connection, true);
                        }
                    }
                }

                var prefetchTask = prefetchConnectionTask;
                prefetchConnectionTask = null;

                // Now create the request
                await handleHttpSessionRequest(endPoint, clientStream, cancellationTokenSource, connectArgs, prefetchTask);
            }
            catch (ProxyException e)
            {
                closeServerConnection = true;
                onException(clientStream, e);
            }
            catch (IOException e)
            {
                closeServerConnection = true;
                onException(clientStream, new Exception("Connection was aborted", e));
            }
            catch (SocketException e)
            {
                closeServerConnection = true;
                onException(clientStream, new Exception("Could not connect", e));
            }
            catch (Exception e)
            {
                closeServerConnection = true;
                onException(clientStream, new Exception("Error occured in whilst handling the client", e));
            }
            finally
            {
                if (!cancellationTokenSource.IsCancellationRequested)
                {
                    cancellationTokenSource.Cancel();
                }

                await tcpConnectionFactory.Release(prefetchConnectionTask, closeServerConnection);

                clientStream.Dispose();
                connectArgs?.Dispose();
            }
        }
Пример #33
0
        /// <summary>
        /// Defines the encryption mode for the transport.
        /// </summary>
        /// <param name="encryption"></param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        /// <exception cref="System.NotSupportedException"></exception>
        public override async Task SetEncryptionAsync(SessionEncryption encryption, CancellationToken cancellationToken)
        {
            await _sendSemaphore.WaitAsync(cancellationToken).ConfigureAwait(false);

            try
            {
                await _receiveSemaphore.WaitAsync(cancellationToken).ConfigureAwait(false);

                try
                {
                    switch (encryption)
                    {
                    case SessionEncryption.None:
                        _stream = _tcpClient.GetStream();
                        break;

                    case SessionEncryption.TLS:
                        SslStream sslStream;

                        if (_serverCertificate != null)
                        {
                            if (_stream == null)
                            {
                                throw new InvalidOperationException("The stream was not initialized. Call OpenAsync first.");
                            }

                            // Server
                            sslStream = new SslStream(
                                _stream,
                                false,
                                _clientCertificateValidationCallback,
                                null,
                                EncryptionPolicy.RequireEncryption);

                            await sslStream
                            .AuthenticateAsServerAsync(
                                _serverCertificate,
                                true,
                                SslProtocols.Tls,
                                false)
                            .WithCancellation(cancellationToken)
                            .ConfigureAwait(false);
                        }
                        else
                        {
                            // Client
                            if (string.IsNullOrWhiteSpace(_hostName))
                            {
                                throw new InvalidOperationException("The hostname is mandatory for TLS client encryption support");
                            }

                            sslStream = new SslStream(
                                _stream,
                                false,
                                _serverCertificateValidationCallback);

                            X509CertificateCollection clientCertificates = null;

                            if (_clientCertificate != null)
                            {
                                clientCertificates = new X509CertificateCollection(new X509Certificate[] { _clientCertificate });
                            }

                            await sslStream
                            .AuthenticateAsClientAsync(
                                _hostName,
                                clientCertificates,
                                SslProtocols.Tls,
                                false)
                            .WithCancellation(cancellationToken)
                            .ConfigureAwait(false);
                        }

                        _stream = sslStream;
                        break;

                    default:
                        throw new NotSupportedException();
                    }

                    Encryption = encryption;
                }
                finally
                {
                    _receiveSemaphore.Release();
                }
            }
            finally
            {
                _sendSemaphore.Release();
            }
        }
        public async Task CertificateValidationClientServer_EndToEnd_Ok(bool useClientSelectionCallback)
        {
            IPEndPoint endPoint = new IPEndPoint(IPAddress.IPv6Loopback, 0);
            var server = new TcpListener(endPoint);
            server.Start();

            _clientCertificateRemovedByFilter = false;

            if (PlatformDetection.IsWindows7 && 
                !useClientSelectionCallback && 
                !Capability.IsTrustedRootCertificateInstalled())
            {
                // https://technet.microsoft.com/en-us/library/hh831771.aspx#BKMK_Changes2012R2
                // Starting with Windows 8, the "Management of trusted issuers for client authentication" has changed:
                // The behavior to send the Trusted Issuers List by default is off.
                //
                // In Windows 7 the Trusted Issuers List is sent within the Server Hello TLS record. This list is built
                // by the server using certificates from the Trusted Root Authorities certificate store.
                // The client side will use the Trusted Issuers List, if not empty, to filter proposed certificates.
                _clientCertificateRemovedByFilter = true;
            }

            using (var clientConnection = new TcpClient(AddressFamily.InterNetworkV6))
            {
                IPEndPoint serverEndPoint = (IPEndPoint)server.LocalEndpoint;

                Task clientConnect = clientConnection.ConnectAsync(serverEndPoint.Address, serverEndPoint.Port);
                Task<TcpClient> serverAccept = server.AcceptTcpClientAsync();

                Assert.True(
                    Task.WaitAll(
                        new Task[] { clientConnect, serverAccept },
                        TestConfiguration.PassingTestTimeoutMilliseconds),
                    "Client/Server TCP Connect timed out.");

                LocalCertificateSelectionCallback clientCertCallback = null;

                if (useClientSelectionCallback)
                {
                    clientCertCallback = ClientCertSelectionCallback;
                }

                using (TcpClient serverConnection = await serverAccept)
                using (SslStream sslClientStream = new SslStream(
                    clientConnection.GetStream(),
                    false,
                    ClientSideRemoteServerCertificateValidation,
                    clientCertCallback))
                using (SslStream sslServerStream = new SslStream(
                    serverConnection.GetStream(),
                    false,
                    ServerSideRemoteClientCertificateValidation))

                {
                    string serverName = _serverCertificate.GetNameInfo(X509NameType.SimpleName, false);
                    var clientCerts = new X509CertificateCollection();

                    if (!useClientSelectionCallback)
                    {
                        clientCerts.Add(_clientCertificate);
                    }

                    Task clientAuthentication = sslClientStream.AuthenticateAsClientAsync(
                        serverName,
                        clientCerts,
                        SslProtocolSupport.DefaultSslProtocols,
                        false);

                    Task serverAuthentication = sslServerStream.AuthenticateAsServerAsync(
                        _serverCertificate,
                        true,
                        SslProtocolSupport.DefaultSslProtocols,
                        false);

                    Assert.True(
                        Task.WaitAll(
                            new Task[] { clientAuthentication, serverAuthentication },
                            TestConfiguration.PassingTestTimeoutMilliseconds),
                        "Client/Server Authentication timed out.");

                    if (!_clientCertificateRemovedByFilter)
                    {
                        Assert.True(sslClientStream.IsMutuallyAuthenticated, "sslClientStream.IsMutuallyAuthenticated");
                        Assert.True(sslServerStream.IsMutuallyAuthenticated, "sslServerStream.IsMutuallyAuthenticated");

                        Assert.Equal(sslServerStream.RemoteCertificate.Subject, _clientCertificate.Subject);
                    }
                    else
                    {
                        Assert.False(sslClientStream.IsMutuallyAuthenticated, "sslClientStream.IsMutuallyAuthenticated");
                        Assert.False(sslServerStream.IsMutuallyAuthenticated, "sslServerStream.IsMutuallyAuthenticated");

                        Assert.Null(sslServerStream.RemoteCertificate);
                    }

                    Assert.Equal(sslClientStream.RemoteCertificate.Subject, _serverCertificate.Subject);
                }
            }
        }
Пример #35
0
        [PlatformSpecific(TestPlatforms.Linux)] // This only applies where OpenSsl is used.
        public async Task SslStream_SendReceiveOverNetworkStream_AuthenticationException()
        {
            SslProtocols clientProtocol;
            SslProtocols serverProtocol;

            // Try to find protocol mismatch.
            if (PlatformDetection.SupportsTls12 && (PlatformDetection.SupportsTls10 || PlatformDetection.SupportsTls11))
            {
                // OpenSSL 1.0 where new is Tls12
                clientProtocol = SslProtocols.Tls | SslProtocols.Tls11;
                serverProtocol = SslProtocols.Tls12;
            }
            else if (PlatformDetection.SupportsTls12 && PlatformDetection.SupportsTls13)
            {
                // OpenSSl 1.1 where new is 1.3 and legacy is 1.2
                clientProtocol = SslProtocols.Tls13;
                serverProtocol = SslProtocols.Tls12;
            }
            else
            {
                throw new SkipTestException("Did not find disjoined sets");
            }

            TcpListener listener = new TcpListener(IPAddress.Loopback, 0);

            using (X509Certificate2 serverCertificate = Configuration.Certificates.GetServerCertificate())
                using (TcpClient client = new TcpClient())
                {
                    listener.Start();

                    Task             clientConnectTask  = client.ConnectAsync(IPAddress.Loopback, ((IPEndPoint)listener.LocalEndpoint).Port);
                    Task <TcpClient> listenerAcceptTask = listener.AcceptTcpClientAsync();

                    await TestConfiguration.WhenAllOrAnyFailedWithTimeout(clientConnectTask, listenerAcceptTask);

                    TcpClient server = listenerAcceptTask.Result;
                    using (SslStream clientStream = new SslStream(
                               client.GetStream(),
                               false,
                               new RemoteCertificateValidationCallback(ValidateServerCertificate),
                               null,
                               EncryptionPolicy.RequireEncryption))
                        using (SslStream serverStream = new SslStream(
                                   server.GetStream(),
                                   false,
                                   null,
                                   null,
                                   EncryptionPolicy.RequireEncryption))
                        {
                            Task clientAuthenticationTask = clientStream.AuthenticateAsClientAsync(
                                serverCertificate.GetNameInfo(X509NameType.SimpleName, false),
                                null,
                                clientProtocol,
                                false);

                            AuthenticationException e = await Assert.ThrowsAsync <AuthenticationException>(() => serverStream.AuthenticateAsServerAsync(
                                                                                                               serverCertificate,
                                                                                                               false,
                                                                                                               serverProtocol,
                                                                                                               false));

                            Assert.NotNull(e.InnerException);
                            Assert.Contains("SSL_ERROR_SSL", e.InnerException.Message);
                            Assert.NotNull(e.InnerException.InnerException);
                            Assert.Contains("protocol", e.InnerException.InnerException.Message);
                        }
                }

            listener.Stop();
        }
Пример #36
0
 protected override bool DoHandshake(SslStream clientSslStream, SslStream serverSslStream)
 {
     using (X509Certificate2 certificate = Configuration.Certificates.GetServerCertificate())
     {
         Task t1 = clientSslStream.AuthenticateAsClientAsync(certificate.GetNameInfo(X509NameType.SimpleName, false));
         Task t2 = serverSslStream.AuthenticateAsServerAsync(certificate);
         return Task.WaitAll(new[] { t1, t2 }, TestConfiguration.PassingTestTimeoutMilliseconds);
     }
 }
Пример #37
0
        //This is called when client is aware of proxy
        //So for HTTPS requests client would send CONNECT header to negotiate a secure tcp tunnel via proxy
        private async Task HandleClient(ExplicitProxyEndPoint endPoint, TcpClient tcpClient)
        {
            Stream clientStream = tcpClient.GetStream();

            clientStream.ReadTimeout  = ConnectionTimeOutSeconds * 1000;
            clientStream.WriteTimeout = ConnectionTimeOutSeconds * 1000;

            var clientStreamReader = new CustomBinaryReader(clientStream);
            var clientStreamWriter = new StreamWriter(clientStream)
            {
                NewLine = ProxyConstants.NewLine
            };

            Uri httpRemoteUri;

            try
            {
                //read the first line HTTP command
                var httpCmd = await clientStreamReader.ReadLineAsync();

                if (string.IsNullOrEmpty(httpCmd))
                {
                    Dispose(clientStream, clientStreamReader, clientStreamWriter, null);
                    return;
                }

                //break up the line into three components (method, remote URL & Http Version)
                var httpCmdSplit = httpCmd.Split(ProxyConstants.SpaceSplit, 3);

                //Find the request Verb
                var httpVerb = httpCmdSplit[0];

                if (httpVerb.ToUpper() == "CONNECT")
                {
                    httpRemoteUri = new Uri("http://" + httpCmdSplit[1]);
                }
                else
                {
                    httpRemoteUri = new Uri(httpCmdSplit[1]);
                }

                //parse the HTTP version
                Version version = new Version(1, 1);
                if (httpCmdSplit.Length == 3)
                {
                    string httpVersion = httpCmdSplit[2].Trim();

                    if (httpVersion == "http/1.0")
                    {
                        version = new Version(1, 0);
                    }
                }
                //filter out excluded host names
                var excluded = endPoint.ExcludedHttpsHostNameRegex != null?
                               endPoint.ExcludedHttpsHostNameRegex.Any(x => Regex.IsMatch(httpRemoteUri.Host, x)) : false;


                List <HttpHeader> connectRequestHeaders = null;

                //Client wants to create a secure tcp tunnel (its a HTTPS request)
                if (httpVerb.ToUpper() == "CONNECT" && !excluded && httpRemoteUri.Port != 80)
                {
                    httpRemoteUri = new Uri("https://" + httpCmdSplit[1]);
                    string tmpLine = null;
                    connectRequestHeaders = new List <HttpHeader>();
                    while (!string.IsNullOrEmpty(tmpLine = await clientStreamReader.ReadLineAsync()))
                    {
                        var header = tmpLine.Split(ProxyConstants.ColonSplit, 2);

                        var newHeader = new HttpHeader(header[0], header[1]);
                        connectRequestHeaders.Add(newHeader);
                    }

                    if (await CheckAuthorization(clientStreamWriter, connectRequestHeaders) == false)
                    {
                        Dispose(clientStream, clientStreamReader, clientStreamWriter, null);
                        return;
                    }

                    await WriteConnectResponse(clientStreamWriter, version);

                    SslStream sslStream = null;

                    try
                    {
                        sslStream = new SslStream(clientStream, true);

                        X509Certificate2 certificate;
                        if (endPoint.GenericCertificate != null)
                        {
                            certificate = endPoint.GenericCertificate;
                        }
                        else
                        {
                            certificate = certificateCacheManager.CreateCertificate(httpRemoteUri.Host, false);
                        }

                        //Successfully managed to authenticate the client using the fake certificate
                        await sslStream.AuthenticateAsServerAsync(certificate, false,
                                                                  SupportedSslProtocols, false);

                        //HTTPS server created - we can now decrypt the client's traffic
                        clientStream = sslStream;

                        clientStreamReader = new CustomBinaryReader(sslStream);
                        clientStreamWriter = new StreamWriter(sslStream)
                        {
                            NewLine = ProxyConstants.NewLine
                        };
                    }
                    catch
                    {
                        if (sslStream != null)
                        {
                            sslStream.Dispose();
                        }

                        Dispose(clientStream, clientStreamReader, clientStreamWriter, null);
                        return;
                    }

                    //Now read the actual HTTPS request line
                    httpCmd = await clientStreamReader.ReadLineAsync();
                }
                //Sorry cannot do a HTTPS request decrypt to port 80 at this time
                else if (httpVerb.ToUpper() == "CONNECT")
                {
                    //Cyphen out CONNECT request headers
                    await clientStreamReader.ReadAllLinesAsync();

                    //write back successfull CONNECT response
                    await WriteConnectResponse(clientStreamWriter, version);

                    await TcpHelper.SendRaw(BUFFER_SIZE, ConnectionTimeOutSeconds, httpRemoteUri.Host, httpRemoteUri.Port,
                                            httpCmd, version, null,
                                            false, SupportedSslProtocols,
                                            new RemoteCertificateValidationCallback(ValidateServerCertificate),
                                            new LocalCertificateSelectionCallback(SelectClientCertificate),
                                            clientStream, tcpConnectionFactory, UpStreamEndPoint);

                    Dispose(clientStream, clientStreamReader, clientStreamWriter, null);
                    return;
                }
                //Now create the request
                await HandleHttpSessionRequest(tcpClient, httpCmd, clientStream, clientStreamReader, clientStreamWriter,
                                               httpRemoteUri.Scheme == Uri.UriSchemeHttps?httpRemoteUri.Host : null, endPoint, connectRequestHeaders, null, null);
            }
            catch (Exception)
            {
                Dispose(clientStream, clientStreamReader, clientStreamWriter, null);
            }
        }
Пример #38
0
        private async Task ServerAsyncSslHelper(
            SslProtocols clientSslProtocols,
            SslProtocols serverSslProtocols,
            bool expectedToFail = false)
        {
            _log.WriteLine(
                "Server: " + serverSslProtocols + "; Client: " + clientSslProtocols +
                " expectedToFail: " + expectedToFail);

            (NetworkStream clientStream, NetworkStream serverStream) = TestHelper.GetConnectedTcpStreams();

            using (SslStream sslServerStream = new SslStream(
                       serverStream,
                       false,
                       AllowEmptyClientCertificate))
                using (SslStream sslClientStream = new SslStream(
                           clientStream,
                           false,
                           delegate {
                    // Allow any certificate from the server.
                    // Note that simply ignoring exceptions from AuthenticateAsClientAsync() is not enough
                    // because in Mono, certificate validation is performed during the handshake and a failure
                    // would result in the connection being terminated before the handshake completed, thus
                    // making the server-side AuthenticateAsServerAsync() fail as well.
                    return(true);
                }))
                {
                    // Use a different SNI for each connection to prevent TLS 1.3 renegotiation issue: https://github.com/dotnet/runtime/issues/47378
                    string serverName = TestHelper.GetTestSNIName(nameof(ServerAsyncSslHelper), clientSslProtocols, serverSslProtocols);

                    _log.WriteLine("Connected on {0} {1} ({2} {3})", clientStream.Socket.LocalEndPoint, clientStream.Socket.RemoteEndPoint, clientStream.Socket.Handle, serverStream.Socket.Handle);
                    _log.WriteLine("client SslStream#{0} server SslStream#{1}", sslClientStream.GetHashCode(), sslServerStream.GetHashCode());

                    _logVerbose.WriteLine("ServerAsyncAuthenticateTest.AuthenticateAsClientAsync start.");
                    Task clientAuthentication = sslClientStream.AuthenticateAsClientAsync(
                        serverName,
                        null,
                        clientSslProtocols,
                        false);

                    _logVerbose.WriteLine("ServerAsyncAuthenticateTest.AuthenticateAsServerAsync start.");
                    Task serverAuthentication = sslServerStream.AuthenticateAsServerAsync(
                        _serverCertificate,
                        true,
                        serverSslProtocols,
                        false);

                    try
                    {
                        await clientAuthentication.WaitAsync(TestConfiguration.PassingTestTimeout);

                        _logVerbose.WriteLine("ServerAsyncAuthenticateTest.clientAuthentication complete.");
                    }
                    catch (Exception ex)
                    {
                        // Ignore client-side errors: we're only interested in server-side behavior.
                        _log.WriteLine("Client exception : " + ex);
                        clientStream.Socket.Shutdown(SocketShutdown.Send);
                    }

                    await serverAuthentication.WaitAsync(TestConfiguration.PassingTestTimeout);

                    _logVerbose.WriteLine("ServerAsyncAuthenticateTest.serverAuthentication complete.");

                    _log.WriteLine(
                        "Server({0}) authenticated with encryption cipher: {1} {2}-bit strength",
                        serverStream.Socket.LocalEndPoint,
                        sslServerStream.CipherAlgorithm,
                        sslServerStream.CipherStrength);

                    Assert.True(
                        sslServerStream.CipherAlgorithm != CipherAlgorithmType.Null,
                        "Cipher algorithm should not be NULL");

                    Assert.True(sslServerStream.CipherStrength > 0, "Cipher strength should be greater than 0");
                }
        }
Пример #39
0
        private async Task <Stream> NegotiateStream(Stream stream)
        {
            if (!_configuration.SslEnabled)
            {
                return(stream);
            }

            var validateRemoteCertificate = new RemoteCertificateValidationCallback(
                (object sender,
                 X509Certificate certificate,
                 X509Chain chain,
                 SslPolicyErrors sslPolicyErrors)
                =>
            {
                if (sslPolicyErrors == SslPolicyErrors.None)
                {
                    return(true);
                }

                if (_configuration.SslPolicyErrorsBypassed)
                {
                    return(true);
                }
                else
                {
                    _log.ErrorFormat("Session [{0}] error occurred when validating remote certificate: [{1}], [{2}].",
                                     this, this.RemoteEndPoint, sslPolicyErrors);
                }

                return(false);
            });

            var sslStream = new SslStream(
                stream,
                false,
                validateRemoteCertificate,
                null,
                _configuration.SslEncryptionPolicy);

            if (!_configuration.SslClientCertificateRequired)
            {
                await sslStream.AuthenticateAsServerAsync(
                    _configuration.SslServerCertificate); // The X509Certificate used to authenticate the server.
            }
            else
            {
                await sslStream.AuthenticateAsServerAsync(
                    _configuration.SslServerCertificate,           // The X509Certificate used to authenticate the server.
                    _configuration.SslClientCertificateRequired,   // A Boolean value that specifies whether the client must supply a certificate for authentication.
                    _configuration.SslEnabledProtocols,            // The SslProtocols value that represents the protocol used for authentication.
                    _configuration.SslCheckCertificateRevocation); // A Boolean value that specifies whether the certificate revocation list is checked during authentication.
            }

            // When authentication succeeds, you must check the IsEncrypted and IsSigned properties
            // to determine what security services are used by the SslStream.
            // Check the IsMutuallyAuthenticated property to determine whether mutual authentication occurred.
            _log.DebugFormat(
                "Ssl Stream: SslProtocol[{0}], IsServer[{1}], IsAuthenticated[{2}], IsEncrypted[{3}], IsSigned[{4}], IsMutuallyAuthenticated[{5}], "
                + "HashAlgorithm[{6}], HashStrength[{7}], KeyExchangeAlgorithm[{8}], KeyExchangeStrength[{9}], CipherAlgorithm[{10}], CipherStrength[{11}].",
                sslStream.SslProtocol,
                sslStream.IsServer,
                sslStream.IsAuthenticated,
                sslStream.IsEncrypted,
                sslStream.IsSigned,
                sslStream.IsMutuallyAuthenticated,
                sslStream.HashAlgorithm,
                sslStream.HashStrength,
                sslStream.KeyExchangeAlgorithm,
                sslStream.KeyExchangeStrength,
                sslStream.CipherAlgorithm,
                sslStream.CipherStrength);

            return(sslStream);
        }
Пример #40
0
        private async Task ServerAsyncSslHelper(
            SslProtocols clientSslProtocols,
            SslProtocols serverSslProtocols,
            bool expectedToFail = false)
        {
            _log.WriteLine(
                "Server: " + serverSslProtocols + "; Client: " + clientSslProtocols +
                " expectedToFail: " + expectedToFail);

            int timeOut = expectedToFail ? TestConfiguration.FailingTestTimeoutMiliseconds
                : TestConfiguration.PassingTestTimeoutMilliseconds;

            IPEndPoint endPoint = new IPEndPoint(IPAddress.Loopback, 0);
            var        server   = new TcpListener(endPoint);

            server.Start();

            using (var clientConnection = new TcpClient())
            {
                IPEndPoint serverEndPoint = (IPEndPoint)server.LocalEndpoint;

                Task             clientConnect = clientConnection.ConnectAsync(serverEndPoint.Address, serverEndPoint.Port);
                Task <TcpClient> serverAccept  = server.AcceptTcpClientAsync();

                // We expect that the network-level connect will always complete.
                await Task.WhenAll(new Task[] { clientConnect, serverAccept }).TimeoutAfter(
                    TestConfiguration.PassingTestTimeoutMilliseconds);

                using (TcpClient serverConnection = await serverAccept)
                    using (SslStream sslServerStream = new SslStream(
                               clientConnection.GetStream(),
                               false,
                               AllowEmptyClientCertificate))
                        using (SslStream sslClientStream = new SslStream(
                                   serverConnection.GetStream(),
                                   false,
                                   delegate {
                            // Allow any certificate from the server.
                            // Note that simply ignoring exceptions from AuthenticateAsClientAsync() is not enough
                            // because in Mono, certificate validation is performed during the handshake and a failure
                            // would result in the connection being terminated before the handshake completed, thus
                            // making the server-side AuthenticateAsServerAsync() fail as well.
                            return(true);
                        }))
                        {
                            string serverName = _serverCertificate.GetNameInfo(X509NameType.SimpleName, false);

                            _logVerbose.WriteLine("ServerAsyncAuthenticateTest.AuthenticateAsClientAsync start.");
                            Task clientAuthentication = sslClientStream.AuthenticateAsClientAsync(
                                serverName,
                                null,
                                clientSslProtocols,
                                false);

                            _logVerbose.WriteLine("ServerAsyncAuthenticateTest.AuthenticateAsServerAsync start.");
                            Task serverAuthentication = sslServerStream.AuthenticateAsServerAsync(
                                _serverCertificate,
                                true,
                                serverSslProtocols,
                                false);

                            try
                            {
                                await clientAuthentication.TimeoutAfter(timeOut);

                                _logVerbose.WriteLine("ServerAsyncAuthenticateTest.clientAuthentication complete.");
                            }
                            catch (Exception ex)
                            {
                                // Ignore client-side errors: we're only interested in server-side behavior.
                                _log.WriteLine("Client exception: " + ex);
                            }

                            await serverAuthentication.TimeoutAfter(timeOut);

                            _logVerbose.WriteLine("ServerAsyncAuthenticateTest.serverAuthentication complete.");

                            _log.WriteLine(
                                "Server({0}) authenticated with encryption cipher: {1} {2}-bit strength",
                                serverEndPoint,
                                sslServerStream.CipherAlgorithm,
                                sslServerStream.CipherStrength);

                            Assert.True(
                                sslServerStream.CipherAlgorithm != CipherAlgorithmType.Null,
                                "Cipher algorithm should not be NULL");

                            Assert.True(sslServerStream.CipherStrength > 0, "Cipher strength should be greater than 0");
                        }
            }
        }
        private bool DoHandshake(SslStream clientSslStream, SslStream serverSslStream, TimeSpan waitTimeSpan)
        {
            X509Certificate2 certificate = TestConfiguration.GetServerCertificate();
            Task[] auth = new Task[2];

            auth[0] = clientSslStream.AuthenticateAsClientAsync(certificate.GetNameInfo(X509NameType.SimpleName, false));
            auth[1] = serverSslStream.AuthenticateAsServerAsync(certificate);

            bool finished = Task.WaitAll(auth, waitTimeSpan);
            return finished;
        }
Пример #42
0
        public async Task SslStream_ServerLocalCertificateSelectionCallbackReturnsNull_Throw()
        {
            VirtualNetwork network = new VirtualNetwork();

            var selectionCallback = new LocalCertificateSelectionCallback((object sender, string targetHost, X509CertificateCollection localCertificates, X509Certificate remoteCertificate, string[] issuers) =>
            {
                return(null);
            });

            using (var clientStream = new VirtualNetworkStream(network, isServer: false))
                using (var serverStream = new VirtualNetworkStream(network, isServer: true))
                    using (var client = new SslStream(clientStream, false, AllowAnyServerCertificate))
                        using (var server = new SslStream(serverStream, false, null, selectionCallback))
                            using (X509Certificate2 certificate = Configuration.Certificates.GetServerCertificate())
                            {
                                var clientJob = client.AuthenticateAsClientAsync(certificate.GetNameInfo(X509NameType.SimpleName, false));

                                await Assert.ThrowsAsync <NotSupportedException>(() => server.AuthenticateAsServerAsync(certificate));

                                // Mono terminates the connection when the server handshake fails.
                                if (PlatformDetection.IsMono)
                                {
                                    await Assert.ThrowsAsync <VirtualNetwork.VirtualNetworkConnectionBroken>(() => clientJob);
                                }
                                else
                                {
                                    await TestConfiguration.WhenAllOrAnyFailedWithTimeout(clientJob);
                                }
                            }
        }
        private bool DoHandshake(SslStream clientSslStream, SslStream serverSslStream)
        {
            using (X509Certificate2 certificate = Configuration.Certificates.GetServerCertificate())
            {
                Task[] auth = new Task[2];

                auth[0] = clientSslStream.AuthenticateAsClientAsync(certificate.GetNameInfo(X509NameType.SimpleName, false));
                auth[1] = serverSslStream.AuthenticateAsServerAsync(certificate);

                bool finished = Task.WaitAll(auth, TestConfiguration.PassingTestTimeoutMilliseconds);
                return finished;
            }
        }
Пример #44
0
        private void OnAccept(Task <TcpClient> result)
        {
            TcpClient client = null;

            // Accept current connection
            try
            {
                client = result.Result;
            }
            catch
            {
            }

            // If we have a connection, then process it
            if (client != null)
            {
                OnClientAccepted(client);

                ClientState state;

                // Start authentication for SSL?
                if (_useSsl)
                {
                    state = new ClientState(client, _sslEncryptionPolicy);
                    _log.WriteLine("Server: starting SSL authentication.");


                    SslStream        sslStream   = null;
                    X509Certificate2 certificate = Configuration.Certificates.GetServerCertificate();

                    try
                    {
                        sslStream = (SslStream)state.Stream;

                        _log.WriteLine("Server: attempting to open SslStream.");
                        sslStream.AuthenticateAsServerAsync(certificate, false, _sslProtocols, false).ContinueWith(t =>
                        {
                            certificate.Dispose();
                            OnAuthenticate(t, state);
                        }, TaskScheduler.Default);
                    }
                    catch (Exception ex)
                    {
                        _log.WriteLine("Server: Exception: {0}", ex);
                        certificate.Dispose();
                        state.Dispose(); // close connection to client
                    }
                }
                else
                {
                    state = new ClientState(client);

                    // Start listening for data from the client connection
                    try
                    {
                        state.Stream.BeginRead(state.ReceiveBuffer, 0, state.ReceiveBuffer.Length, OnReceive, state);
                    }
                    catch
                    {
                    }
                }
            }

            // Listen for more client connections
            try
            {
                _listener.AcceptTcpClientAsync().ContinueWith(t => OnAccept(t), TaskScheduler.Default);
            }
            catch
            {
            }
        }
Пример #45
0
        private async Task ServerAsyncSslHelper(
            SslProtocols clientSslProtocols,
            SslProtocols serverSslProtocols,
            bool expectedToFail = false)
        {
            _log.WriteLine(
                "Server: " + serverSslProtocols + "; Client: " + clientSslProtocols +
                " expectedToFail: " + expectedToFail);

            int timeOut = expectedToFail ? TestConfiguration.FailingTestTimeoutMiliseconds
                : TestConfiguration.PassingTestTimeoutMilliseconds;

            IPEndPoint endPoint = new IPEndPoint(IPAddress.IPv6Loopback, 0);
            var server = new TcpListener(endPoint);
            server.Start();

            using (var clientConnection = new TcpClient(AddressFamily.InterNetworkV6))
            {
                IPEndPoint serverEndPoint = (IPEndPoint)server.LocalEndpoint;

                Task clientConnect = clientConnection.ConnectAsync(serverEndPoint.Address, serverEndPoint.Port);
                Task<TcpClient> serverAccept = server.AcceptTcpClientAsync();

                // We expect that the network-level connect will always complete.
                await Task.WhenAll(new Task[] { clientConnect, serverAccept }).TimeoutAfter(
                    TestConfiguration.PassingTestTimeoutMilliseconds);

                using (TcpClient serverConnection = await serverAccept)
                using (SslStream sslClientStream = new SslStream(clientConnection.GetStream()))
                using (SslStream sslServerStream = new SslStream(
                    serverConnection.GetStream(),
                    false,
                    AllowAnyServerCertificate))
                {
                    string serverName = _serverCertificate.GetNameInfo(X509NameType.SimpleName, false);

                    _logVerbose.WriteLine("ServerAsyncAuthenticateTest.AuthenticateAsClientAsync start.");
                    Task clientAuthentication = sslClientStream.AuthenticateAsClientAsync(
                        serverName,
                        null,
                        clientSslProtocols,
                        false);

                    _logVerbose.WriteLine("ServerAsyncAuthenticateTest.AuthenticateAsServerAsync start.");
                    Task serverAuthentication = sslServerStream.AuthenticateAsServerAsync(
                        _serverCertificate,
                        true,
                        serverSslProtocols,
                        false);

                    try
                    {
                        await clientAuthentication.TimeoutAfter(timeOut);
                        _logVerbose.WriteLine("ServerAsyncAuthenticateTest.clientAuthentication complete.");
                    }
                    catch (Exception ex)
                    {
                        // Ignore client-side errors: we're only interested in server-side behavior.
                        _log.WriteLine("Client exception: " + ex);
                    }

                    await serverAuthentication.TimeoutAfter(timeOut);
                    _logVerbose.WriteLine("ServerAsyncAuthenticateTest.serverAuthentication complete.");

                    _log.WriteLine(
                        "Server({0}) authenticated with encryption cipher: {1} {2}-bit strength",
                        serverEndPoint,
                        sslServerStream.CipherAlgorithm,
                        sslServerStream.CipherStrength);

                    Assert.True(
                        sslServerStream.CipherAlgorithm != CipherAlgorithmType.Null,
                        "Cipher algorithm should not be NULL");

                    Assert.True(sslServerStream.CipherStrength > 0, "Cipher strength should be greater than 0");
                }
            }
        }
Пример #46
0
        public async Task SslStream_StreamToStream_DataAfterShutdown_Fail()
        {
            VirtualNetwork network = new VirtualNetwork();

            using (var clientStream = new VirtualNetworkStream(network, isServer: false))
            using (var serverStream = new VirtualNetworkStream(network, isServer: true))
            using (var client = new SslStream(clientStream, true, AllowAnyServerCertificate))
            using (var server = new SslStream(serverStream))
            using (X509Certificate2 certificate = Configuration.Certificates.GetServerCertificate())
            {
                var handshake = new Task[2];

                handshake[0] = server.AuthenticateAsServerAsync(certificate);
                handshake[1] = client.AuthenticateAsClientAsync(certificate.GetNameInfo(X509NameType.SimpleName, false));

                await Task.WhenAll(handshake).TimeoutAfter(TestConfiguration.PassingTestTimeoutMilliseconds);

                var buffer = new byte[1024];

                Assert.Equal(true, client.CanWrite);

                await client.ShutdownAsync();

                Assert.Equal(false, client.CanWrite);

                await Assert.ThrowsAsync<InvalidOperationException>(() => client.ShutdownAsync());
                await Assert.ThrowsAsync<InvalidOperationException>(() => client.WriteAsync(buffer, 0, buffer.Length));
            }
        }