NegotiateStream Class
Provides a stream that uses the Negotiate security protocol to authenticate the client, and optionally the server, in client-server communication.
Assembly: System (in System.dll)
Use the NegotiateStream class for authentication and to help secure information transmitted between a client and a server. Using NegotiateStream, you can do the following.
Send the client's credentials to the server for Impersonation or Delegation.
Request server authentication.
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. For detailed descriptions of these protocols, see the Platform SDK documentation on MSDN, at msdn.microsoft.com/library/. 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.
The following code example demonstrates the client side of a client-server connection that uses the NegotiateStream. The client authenticates and sends a message to the server asynchronously.
#using <mscorlib.dll>
#using <System.dll>
using namespace System;
using namespace System::Net;
using namespace System::Net::Security;
using namespace System::Net::Sockets;
using namespace System::Text;
// The following class displays the properties of an authenticatedStream.
public __gc class AuthenticatedStreamReporter
{
public:
static void DisplayProperties(AuthenticatedStream* stream)
{
Console::WriteLine(S"IsAuthenticated: {0}", __box(stream->IsAuthenticated));
Console::WriteLine(S"IsMutuallyAuthenticated: {0}", __box(stream->IsMutuallyAuthenticated));
Console::WriteLine(S"IsEncrypted: {0}", __box(stream->IsEncrypted));
Console::WriteLine(S"IsSigned: {0}", __box(stream->IsSigned));
Console::WriteLine(S"IsServer: {0}", __box(stream->IsServer));
}
};
public __gc class ASynchronousAuthenticatingTcpClient
{
static TcpClient* client = 0;
public:
void TestAuthenticatingClient()
{
// Establish the remote endpoint for the socket.
// For this example, use the local machine.
IPHostEntry* ipHostInfo = Dns::Resolve(Dns::GetHostName());
IPAddress* ipAddress = ipHostInfo->AddressList[0];
// Client and server use port 11000.
IPEndPoint* remoteEP = new IPEndPoint(ipAddress, 11000);
// Create a TCP/IP socket.
client = new TcpClient();
// Connect the socket to the remote endpoint.
client->Connect(remoteEP);
Console::WriteLine(S"Client connected to {0}.", remoteEP);
// Ensure the client does not close when there is
// still data to be sent to the server.
client->LingerState = (new LingerOption(true, 0));
// Request authentication.
NetworkStream* clientStream = client->GetStream();
NegotiateStream* authStream = new NegotiateStream(clientStream, false);
// Pass the NegotiateStream as the AsyncState object
// so that it is available to the callback delegate.
IAsyncResult* ar = authStream->BeginClientAuthenticate(
new AsyncCallback(EndAuthenticateCallback),
authStream
);
Console::WriteLine(S"Client waiting for authentication...");
// Wait until the result is available.
ar->AsyncWaitHandle->WaitOne();
// Display the properties of the authenticated stream.
AuthenticatedStreamReporter::DisplayProperties(authStream);
// Send a message to the server.
// Encode the test data into a byte array.
Byte message[] = Encoding::UTF8->GetBytes(S"Hello from the client.");
ar = authStream->BeginWrite(message, 0, message->Length,
new AsyncCallback(EndWriteCallback),
authStream);
ar->AsyncWaitHandle->WaitOne();
Console::WriteLine(S"Sent {0} bytes.", __box(message->Length));
// Close the client connection.
authStream->Close();
Console::WriteLine(S"Client closed.");
}
// The following method is called when the authentication completes.
public:
static void EndAuthenticateCallback (IAsyncResult* ar)
{
Console::WriteLine(S"Client ending authentication...");
NegotiateStream* authStream = dynamic_cast<NegotiateStream*> (ar->AsyncState);
// End the asynchronous operation.
authStream->EndClientAuthenticate(ar);
// Console.WriteLine("AllowedImpersonation: {0}", authStream.AllowedImpersonation);
}
// The following method is called when the write operation completes.
public:
static void EndWriteCallback (IAsyncResult* ar)
{
Console::WriteLine(S"Client ending write operation...");
NegotiateStream* authStream = dynamic_cast<NegotiateStream*> (ar->AsyncState);
// End the asynchronous operation.
authStream->EndWrite(ar);
}
};
void main(){
ASynchronousAuthenticatingTcpClient* aatc = new ASynchronousAuthenticatingTcpClient;
aatc->TestAuthenticatingClient();
}
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.
#using <mscorlib.dll>
#using <System.dll>
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 __gc class ClientState
{
private:
AuthenticatedStream* authStream;
private:
TcpClient* client;
Byte buffer[];
StringBuilder* message;
ManualResetEvent* waiter;
public private:
ClientState(AuthenticatedStream* a, TcpClient* theClient)
{
authStream = a;
client = theClient;
message = 0;
buffer = new Byte[2048];
waiter = new ManualResetEvent(false);
}
public private:
__property TcpClient* get_Client() { return client;}
public private:
__property AuthenticatedStream* get_AuthenticatedStream() { return authStream;}
public private:
__property Byte get_Buffer()[] { return buffer;}
public private:
__property StringBuilder* get_Message()
{
if (message == 0)
message = new StringBuilder();
return message;
}
public private:
__property ManualResetEvent* get_Waiter()
{
return waiter;
}
};
public __gc class AsynchronousAuthenticatingTcpListener
{
public:
int TestAuthenticatingListener()
{
// Create an IPv4 TCP/IP socket.
TcpListener* listener = new TcpListener(IPAddress::Any, 11000);
// Listen for incoming connections.
listener->Start();
while (true)
{
TcpClient* clientRequest = 0;
// Application blocks while waiting for an incoming connection.
// Type CNTL-C to terminate the server.
clientRequest = listener->AcceptTcpClient();
Console::WriteLine(S"Client connected.");
// 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->BeginServerAuthenticate (
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
// BeginServerAuthenticate callback delegate.
public:
static void EndAuthenticateCallback (IAsyncResult* ar)
{
// Get the saved data.
ClientState* cState = dynamic_cast<ClientState*> (ar->AsyncState);
TcpClient* clientRequest = cState->Client;
NegotiateStream* authStream = dynamic_cast<NegotiateStream*> (cState->AuthenticatedStream);
Console::WriteLine(S"Ending authentication.");
// Any exceptions that occurred during authentication are
// thrown by the EndServerAuthenticate method.
try
{
// This call blocks until the authentication is complete.
authStream->EndServerAuthenticate(ar);
}
catch (AuthenticationException* e)
{
Console::WriteLine(e);
Console::WriteLine(S"Authentication failed - closing connection.");
cState->Waiter->Set();
return;
}
catch (Exception* e)
{
Console::WriteLine(e);
Console::WriteLine(S"Closing connection.");
cState->Waiter->Set();
return;
}
// Display properties of the authenticated client.
IIdentity* id = authStream->RemoteIdentity;
Console::WriteLine(S"{0} was authenticated using {1}.",
id->Name,
id->AuthenticationType
);
cState->Waiter->Set();
}
public:
static void EndReadCallback(IAsyncResult* ar)
{
// Get the saved data.
ClientState* cState = dynamic_cast<ClientState*> (ar->AsyncState);
TcpClient* clientRequest = cState->Client;
NegotiateStream* authStream = dynamic_cast<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(S"Client message exception:");
Console::WriteLine(e);
cState->Waiter->Set();
return;
}
IIdentity* id = authStream->RemoteIdentity;
Console::WriteLine(S"{0} says {1}", id->Name, cState->Message);
cState->Waiter->Set();
}
};
void main(){
AsynchronousAuthenticatingTcpListener* aatl = new AsynchronousAuthenticatingTcpListener;
aatl->TestAuthenticatingListener();
}
System.MarshalByRefObject
System.IO.Stream
System.Net.Security.AuthenticatedStream
System.Net.Security.NegotiateStream
Windows 7, Windows Vista, Windows XP SP2, Windows XP Media Center Edition, Windows XP Professional x64 Edition, Windows XP Starter Edition, Windows Server 2008 R2, Windows Server 2008, Windows Server 2003, Windows Server 2000 SP4, Windows Millennium Edition, Windows 98
The .NET Framework and .NET Compact Framework do not support all versions of every platform. For a list of the supported versions, see .NET Framework System Requirements.