< SSLStream Client Examples | Main | NegotiateStream Class Client Example >

 


 

Chapter 2 Part 12:

Managed I/O - Streams, Readers, and Writers

 

 

What do we have in this chapter 2 Part 12?

  1. NegotiateStream Class

  2. C++ NegotiateStream Server Program Example

  3. C# NegotiateStream Server Program Example

 

 

 

Note: If you want to experience a complete C++ .NET/C++-CLI programming tutorial please jump to Visual C++ .NET programming tutorial.

 

NegotiateStream Class

 

NegotiateStream provides a stream that uses the Negotiate security protocol to authenticate the client, and optionally the server, in client-server communication. The NegotiateStream class used for authentication and to help secure information transmitted between a client and a server. Using NegotiateStream, you can do the following.

 

  1. Send the client's credentials to the server for Impersonation or Delegation.
  2. Request server authentication.
  3. Encrypt and/or sign data before transmitting it.

 

Authentication must be performed before transmitting information. Clients request authentication using the synchronous AuthenticateAsClient methods, which block until the authentication completes, or the asynchronous BeginAuthenticateAsClient methods, which do not block while waiting for the authentication to complete. Servers request authentication using the synchronous AuthenticateAsServer or asynchronous BeginAuthenticateAsServer methods. The client, and optionally the server, is authenticated using the Negotiate security protocol. On Windows 95/98 systems, Windows NT LAN Manager (NTLM) is the protocol used for authentication. On other platforms the Kerberos protocol is used for authentication if both client and server support it; otherwise NTLM is used. The NegotiateStream class performs the authentication using the Security Support Provider Interface (SSPI).

When authentication succeeds, you must check the IsEncrypted and IsSigned properties to determine what security services will be used by the NegotiateStream to help secure your data during transmission. Check the IsMutuallyAuthenticated property to determine whether mutual authentication occurred. You can get information about the remote client or server using the RemoteIdentity property.

If the authentication fails, you will receive an AuthenticationException or a InvalidCredentialException. In this case, you can retry the authentication with a different credential.

You send data using the synchronous Write() or asynchronous BeginWrite() methods. You receive data using the synchronous Read() or asynchronous BeginRead() methods. If security services such as encryption or signing are enabled, these are automatically applied to your data by the NegotiateStream.

The NegotiateStream transmits data using a stream that you supply when creating the NegotiateStream. When you supply this underlying stream, you have the option to specify whether closing the NegotiateStream also closes the underlying stream.

 

C++ NegotiateStream Server Example

 

Create a new CLR console application project and you might want to use NegotiatedStreamServerCP as the project and solution names.

 

C++ NegotiateStream Server Example - new CLR console project creation

 

Add the following code.

 

// NegotiatedStreamServerCP.cpp : main project file.

 

#include "stdafx.h"

 

using namespace System;

using namespace System::Net;

using namespace System::Net::Security;

using namespace System::Net::Sockets;

using namespace System::Security::Authentication;

using namespace System::Security::Principal;

using namespace System::Text;

using namespace System::IO;

using namespace System::Threading;

 

// ClientState is the AsyncState object.

private ref class ClientState

{

            private:

                        AuthenticatedStream^ authStream;

                        TcpClient^ client;

                        array<Byte>^buffer;

                        StringBuilder^ message;

                        ManualResetEvent^ waiter;

 

            internal:

                        ClientState( AuthenticatedStream^ a, TcpClient^ theClient )

                        {

                                    authStream = a;

                                    client = theClient;

                                    message = nullptr;

                                    buffer = gcnew array<Byte>(2048);

                                    waiter = gcnew ManualResetEvent( false );

                        }

 

            internal:

                        property TcpClient^ Client

                        {

                                    TcpClient^ get()

                                    {

                                                return client;

                                    }

                        }

                       

                        property AuthenticatedStream^ AuthStream

                        {

                                    AuthenticatedStream^ get()

                                    {

                                                return authStream;

                                    }

                        }

                       

                        property array<Byte>^ Buffer

                        {

                                    array<Byte>^ get()

                                    {

                                                return buffer;

                                    }

                        }

 

                        property StringBuilder^ Message

                        {

                                    StringBuilder^ get()

                                    {

                                                if ( message == nullptr )

                                                            message = gcnew StringBuilder;

                                                return message;

                                    }

                        }

                       

                        property ManualResetEvent^ Waiter

                        {

                                    ManualResetEvent^ get()

                                    {

                                                return waiter;

                                    }

                        }

};

 

public ref class AsynchronousAuthenticatingTcpListener

{

            public:

                        int AnotherDayAnotherMain()

                        {

                                    // Create an IPv4 TCP/IP socket.

                                    Console::WriteLine( L"Creating an IPv4 TCP/IP socket..." );

                                    // Listen for specific IP

                                    String^ Addr = "127.0.0.1";

                                    TcpListener^ listener = gcnew TcpListener(IPAddress::Parse(Addr), 1234);

                                    // Or may listen to any interface...

                                    // TcpListener^ listener = gcnew TcpListener(IPAddress::Any,1234 );

 

                                    // Listen for incoming connections.

                                    Console::WriteLine( L"Listening for incoming connections on {0}:1234", IPAddress::Parse(Addr));

                                    listener->Start();

                                    while ( true )

                                    {

                                                TcpClient^ clientRequest = nullptr;

                                               

                                                // Application blocks while waiting for an incoming connection.

                                                // Press CNTL-C to terminate the server.

                                                clientRequest = listener->AcceptTcpClient();

                                                Console::WriteLine( L"Client connected..." );

 

                                                // A client has connected.

                                                try

                                                {

                                                            AuthenticateClient( clientRequest );

                                                }

                                                catch ( Exception^ e )

                                                {

                                                            Console::WriteLine( e );

                                                            continue;

                                                }

                                    }

                        }

                       

                        static void AuthenticateClient( TcpClient^ clientRequest )

                        {

                                    NetworkStream^ stream = clientRequest->GetStream();

                                   

                                    // Create the NegotiateStream.

                                    Console::WriteLine( L"Creating the NegotiateStream..." );

                                    NegotiateStream^ authStream = gcnew NegotiateStream( stream, false );

                                   

                                    // Save the current client and NegotiateStream instance in a ClientState object.

                                    Console::WriteLine( L"Saving the current client and NegotiateStream instance in a ClientState object...." );

                                    ClientState^ cState = gcnew ClientState( authStream, clientRequest );

                                   

                                    // Listen for the client authentication request.

                                    Console::WriteLine( L"Listening for the client authentication request..." );

                                    authStream->BeginAuthenticateAsServer( gcnew AsyncCallback( EndAuthenticateCallback ), cState );

                                   

                                    // Wait until the authentication completes.

                                    Console::WriteLine( L"Wait until the authentication completes...." );

                                    cState->Waiter->WaitOne();

                                    cState->Waiter->Reset();

                                    authStream->BeginRead(cState->Buffer, 0, cState->Buffer->Length,

                                                                                 gcnew AsyncCallback( EndReadCallback ), cState );

                                    cState->Waiter->WaitOne();

                                   

                                    // Finished with the current client.

                                    Console::WriteLine( L"Finished with the current client....." );

                                    authStream->Close();

                                    clientRequest->Close();

                        }

 

                        // The following method is invoked by the BeginServerAuthenticate callback delegate.

                        static void EndAuthenticateCallback( IAsyncResult^ ar )

                        {

                                    // Get the saved data.

                                    Console::WriteLine( L"Getting the saved data..." );

                                    ClientState^ cState = dynamic_cast<ClientState^>(ar->AsyncState);

                                    TcpClient^ clientRequest = cState->Client;

                                    NegotiateStream^ authStream = dynamic_cast<NegotiateStream^>(cState->AuthStream);

                                    Console::WriteLine( L"Ending authentication." );

                                   

                                    // Any exceptions that occurred during authentication are thrown by the EndServerAuthenticate method.

                                    try

                                    {

                                                // This call blocks until the authentication is complete.

                                                authStream->EndAuthenticateAsServer( ar );

                                    }

                                    catch ( AuthenticationException^ e )

                                    {

                                                Console::WriteLine( e );

                                                Console::WriteLine( L"Authentication failed - closing connection." );

                                                cState->Waiter->Set();

                                                return;

                                    }

                                    catch ( Exception^ e )

                                    {

                                                Console::WriteLine( e );

                                                Console::WriteLine( L"Closing connection." );

                                                cState->Waiter->Set();

                                                return;

                                    }

                                    // Display properties of the authenticated client.

                                    IIdentity^ id = authStream->RemoteIdentity;

                                    Console::WriteLine( L"{0} was authenticated using {1}.", id->Name, id->AuthenticationType );

                                    cState->Waiter->Set();

                        }

 

                        static void EndReadCallback( IAsyncResult^ ar )

                        {

                                    // Get the saved data.

                                    Console::WriteLine( L"Getting the saved data..." );

                                    ClientState^ cState = dynamic_cast<ClientState^>(ar->AsyncState);

                                    TcpClient^ clientRequest = cState->Client;

                                    NegotiateStream^ authStream = dynamic_cast<NegotiateStream^>(cState->AuthStream);

                                   

                                    // Get the buffer that stores the message sent by the client.

                                    Console::WriteLine( L"Getting the buffer that stores the message sent by the client..." );

                                    int bytes = -1;

                                   

                                    // Read the client message.

                                    Console::WriteLine( L"Reading the client message..." );

                                    try

                                    {

                                                bytes = authStream->EndRead( ar );

                                                cState->Message->Append( Encoding::UTF8->GetChars( cState->Buffer, 0, bytes ) );

                                                if ( bytes != 0 )

                                                {

                                                            authStream->BeginRead(cState->Buffer, 0, cState->Buffer->Length,

                                                                                                          gcnew AsyncCallback( EndReadCallback ), cState );

                                                            return;

                                                }

                                    }

                                    catch ( Exception^ e )

                                    {

                                                // A real application should do something useful here, such as logging the failure.

                                                Console::WriteLine( L"Client message exception:" );

                                                Console::WriteLine( e );

                                                cState->Waiter->Set();

                                                return;

                                    }

                                    IIdentity^ id = authStream->RemoteIdentity;

                                    Console::WriteLine( L"{0} server says {1}", id->Name, cState->Message );

                                    cState->Waiter->Set();

                        }

};

 

int main(array<System::String ^> ^args)

{

            AsynchronousAuthenticatingTcpListener^ aatl = gcnew AsynchronousAuthenticatingTcpListener;

            aatl->AnotherDayAnotherMain();

            return 0;

}

 

 

 

 

Build and run the project. The following is an output example.

 

C++ NegotiateStream Server Example - a server program that start listening for connections

 

C# NegotiateStream Server Example

 

The following code example demonstrates the server side of a client-server connection that uses the NegotiateStream to authenticate the client and read a message sent by the client. Create a new console application project. You can use the solution and project name as shown in the following Figure.

 

C# NegotiateStream Server Example -a new console mode project creation  

 

Then, add the following code.

 

using System.Collections.Generic;

using System;

using System.Net;

using System.Net.Security;

using System.Net.Sockets;

using System.Security.Authentication;

using System.Security.Principal;

using System.Text;

using System.IO;

using System.Threading;

 

namespace NegotiateStreamServerCS

{

    public class AsynchronousAuthenticatingTcpListener

    {

        public static void Main()

        {

            // Create an IPv4 TCP/IP socket.

            String Addr = "127.0.0.1";

            TcpListener listener = new TcpListener(IPAddress.Parse(Addr), 11000);

            // Listen for incoming connections.

            listener.Start();

            Console.WriteLine("I'm listening for client connection...");

            while (true)

            {

                TcpClient clientRequest = null;

                // Application blocks while waiting for an incoming connection. Press CNTL-C to terminate the server.

                clientRequest = listener.AcceptTcpClient();

                Console.WriteLine("Client connected...");

                Console.WriteLine("IP: {0}, Port: 11000...", Addr);

                // A client has connected.

                try

                {

                    AuthenticateClient(clientRequest);

                }

                catch (Exception e)

                {

                    Console.WriteLine(e);

                    continue;

                }

            }

        }

        public static void AuthenticateClient(TcpClient clientRequest)

        {

            NetworkStream stream = clientRequest.GetStream();

            // Create the NegotiateStream.

            NegotiateStream authStream = new NegotiateStream(stream, false);

            // Save the current client and NegotiateStream instance in a ClientState object.

            ClientState cState = new ClientState(authStream, clientRequest);

            // Listen for the client authentication request.

            authStream.BeginAuthenticateAsServer(new AsyncCallback(EndAuthenticateCallback), cState);

            // Wait until the authentication completes.

            cState.Waiter.WaitOne();

            cState.Waiter.Reset();

            authStream.BeginRead(cState.Buffer, 0, cState.Buffer.Length, new AsyncCallback(EndReadCallback), cState);

            cState.Waiter.WaitOne();

            // Finished with the current client.

            authStream.Close();

            clientRequest.Close();

        }

 

        // The following method is invoked by the BeginAuthenticateAsServer callback delegate.

        public static void EndAuthenticateCallback(IAsyncResult ar)

        {

            // Get the saved data.

            ClientState cState = (ClientState)ar.AsyncState;

            TcpClient clientRequest = cState.Client;

            NegotiateStream authStream = (NegotiateStream)cState.AuthenticatedStream;

            Console.WriteLine("Ending authentication.");

            // Any exceptions that occurred during authentication are thrown by the EndAuthenticateAsServer method.

            try

            {

                // This call blocks until the authentication is complete.

                authStream.EndAuthenticateAsServer(ar);

            }

            catch (AuthenticationException e)

            {

                Console.WriteLine(e);

                Console.WriteLine("Authentication failed - closing connection.");

                cState.Waiter.Set();

                return;

            }

            catch (Exception e)

            {

                Console.WriteLine(e);

                Console.WriteLine("Closing connection.");

                cState.Waiter.Set();

                return;

            }

            // Display properties of the authenticated client.

            IIdentity id = authStream.RemoteIdentity;

            Console.WriteLine("{0} was authenticated using {1}.", id.Name, id.AuthenticationType);

            cState.Waiter.Set();

        }

        public static void EndReadCallback(IAsyncResult ar)

        {

            // Get the saved data.

            ClientState cState = (ClientState)ar.AsyncState;

            TcpClient clientRequest = cState.Client;

            NegotiateStream authStream = (NegotiateStream)cState.AuthenticatedStream;

            // Get the buffer that stores the message sent by the client.

            int bytes = -1;

            // Read the client message.

            try

            {

                bytes = authStream.EndRead(ar);

                cState.Message.Append(Encoding.UTF8.GetChars(cState.Buffer, 0, bytes));

                if (bytes != 0)

                {

                    authStream.BeginRead(cState.Buffer, 0, cState.Buffer.Length,

                          new AsyncCallback(EndReadCallback), cState);

                    return;

                }

            }

            catch (Exception e)

            {

                // A real application should do something useful here, such as logging the failure.

                Console.WriteLine("Client message exception:");

                Console.WriteLine(e);

                cState.Waiter.Set();

                return;

            }

            IIdentity id = authStream.RemoteIdentity;

            Console.WriteLine("{0} says {1}", id.Name, cState.Message.ToString());

            cState.Waiter.Set();

        }

    }

    // ClientState is the AsyncState object.

    internal class ClientState

    {

        private AuthenticatedStream authStream = null;

        private TcpClient client = null;

        byte[ ] buffer = new byte[2048];

        StringBuilder message = null;

        ManualResetEvent waiter = new ManualResetEvent(false);

        internal ClientState(AuthenticatedStream a, TcpClient theClient)

        {

            authStream = a;

            client = theClient;

        }

        internal TcpClient Client

        {

            get { return client; }

        }

        internal AuthenticatedStream AuthenticatedStream

        {

            get { return authStream; }

        }

        internal byte[] Buffer

        {

            get { return buffer; }

        }

        internal StringBuilder Message

        {

            get

            {

                if (message == null)

                    message = new StringBuilder();

                return message;

            }

        }

        internal ManualResetEvent Waiter

        {

            get

            {

                return waiter;

            }

        }

    }

}

 

An output sample:

 

C# NegotiateStream Server Example - output example

 

 

 


 

< SSLStream Client Examples | Main | NegotiateStream Class Client Example >