2 out of 7 rated this helpful - Rate this topic

SslStream Class

Provides a stream used for client-server communication that uses the Secure Socket Layer (SSL) security protocol to authenticate the server and optionally the client.

System.Object
  System.MarshalByRefObject
    System.IO.Stream
      System.Net.Security.AuthenticatedStream
        System.Net.Security.SslStream

Namespace:  System.Net.Security
Assembly:  System (in System.dll)
public class SslStream : AuthenticatedStream

The SslStream type exposes the following members.

  Name Description
Public method SslStream(Stream) Initializes a new instance of the SslStream class using the specified Stream.
Public method SslStream(Stream, Boolean) Initializes a new instance of the SslStream class using the specified Stream and stream closure behavior.
Public method SslStream(Stream, Boolean, RemoteCertificateValidationCallback) Initializes a new instance of the SslStream class using the specified Stream, stream closure behavior and certificate validation delegate.
Public method SslStream(Stream, Boolean, RemoteCertificateValidationCallback, LocalCertificateSelectionCallback) Initializes a new instance of the SslStream class using the specified Stream, stream closure behavior, certificate validation delegate and certificate selection delegate.
Public method SslStream(Stream, Boolean, RemoteCertificateValidationCallback, LocalCertificateSelectionCallback, EncryptionPolicy) Initializes a new instance of the SslStream class using the specified Stream
Top
  Name Description
Public property CanRead Gets a Boolean value that indicates whether the underlying stream is readable. (Overrides Stream.CanRead.)
Public property CanSeek Gets a Boolean value that indicates whether the underlying stream is seekable. (Overrides Stream.CanSeek.)
Public property CanTimeout Gets a Boolean value that indicates whether the underlying stream supports time-outs. (Overrides Stream.CanTimeout.)
Public property CanWrite Gets a Boolean value that indicates whether the underlying stream is writable. (Overrides Stream.CanWrite.)
Public property CheckCertRevocationStatus Gets a Boolean value that indicates whether the certificate revocation list is checked during the certificate validation process.
Public property CipherAlgorithm Gets a value that identifies the bulk encryption algorithm used by this SslStream.
Public property CipherStrength Gets a value that identifies the strength of the cipher algorithm used by this SslStream.
Public property HashAlgorithm Gets the algorithm used for generating message authentication codes (MACs).
Public property HashStrength Gets a value that identifies the strength of the hash algorithm used by this instance.
Protected property InnerStream Gets the stream used by this AuthenticatedStream for sending and receiving data. (Inherited from AuthenticatedStream.)
Public property IsAuthenticated Gets a Boolean value that indicates whether authentication was successful. (Overrides AuthenticatedStream.IsAuthenticated.)
Public property IsEncrypted Gets a Boolean value that indicates whether this SslStream uses data encryption. (Overrides AuthenticatedStream.IsEncrypted.)
Public property IsMutuallyAuthenticated Gets a Boolean value that indicates whether both server and client have been authenticated. (Overrides AuthenticatedStream.IsMutuallyAuthenticated.)
Public property IsServer Gets a Boolean value that indicates whether the local side of the connection used by this SslStream was authenticated as the server. (Overrides AuthenticatedStream.IsServer.)
Public property IsSigned Gets a Boolean value that indicates whether the data sent using this stream is signed. (Overrides AuthenticatedStream.IsSigned.)
Public property KeyExchangeAlgorithm Gets the key exchange algorithm used by this SslStream.
Public property KeyExchangeStrength Gets a value that identifies the strength of the key exchange algorithm used by this instance.
Public property LeaveInnerStreamOpen Gets whether the stream used by this AuthenticatedStream for sending and receiving data has been left open. (Inherited from AuthenticatedStream.)
Public property Length Gets the length of the underlying stream. (Overrides Stream.Length.)
Public property LocalCertificate Gets the certificate used to authenticate the local endpoint.
Public property Position Gets or sets the current position in the underlying stream. (Overrides Stream.Position.)
Public property ReadTimeout Gets or sets the amount of time a read operation blocks waiting for data. (Overrides Stream.ReadTimeout.)
Public property RemoteCertificate Gets the certificate used to authenticate the remote endpoint.
Public property SslProtocol Gets a value that indicates the security protocol used to authenticate this connection.
Public property TransportContext Gets the TransportContext used for authentication using extended protection.
Public property WriteTimeout Gets or sets the amount of time a write operation blocks waiting for data. (Overrides Stream.WriteTimeout.)
Top
  Name Description
Public method AuthenticateAsClient(String) Called by clients to authenticate the server and optionally the client in a client-server connection.
Public method AuthenticateAsClient(String, X509CertificateCollection, SslProtocols, Boolean) Called by clients to authenticate the server and optionally the client in a client-server connection. The authentication process uses the specified certificate collection and SSL protocol.
Public method AuthenticateAsServer(X509Certificate) Called by servers to authenticate the server and optionally the client in a client-server connection using the specified certificate.
Public method AuthenticateAsServer(X509Certificate, Boolean, SslProtocols, Boolean) Called by servers to begin an asynchronous operation to authenticate the server and optionally the client using the specified certificates, requirements and security protocol.
Public method BeginAuthenticateAsClient(String, AsyncCallback, Object) Called by clients to begin an asynchronous operation to authenticate the server and optionally the client.
Public method BeginAuthenticateAsClient(String, X509CertificateCollection, SslProtocols, Boolean, AsyncCallback, Object) Called by clients to begin an asynchronous operation to authenticate the server and optionally the client using the specified certificates and security protocol.
Public method BeginAuthenticateAsServer(X509Certificate, AsyncCallback, Object) Called by servers to begin an asynchronous operation to authenticate the client and optionally the server in a client-server connection.
Public method BeginAuthenticateAsServer(X509Certificate, Boolean, SslProtocols, Boolean, AsyncCallback, Object) Called by servers to begin an asynchronous operation to authenticate the server and optionally the client using the specified certificates, requirements and security protocol.
Public method BeginRead Begins an asynchronous read operation that reads data from the stream and stores it in the specified array. (Overrides Stream.BeginRead(Byte[], Int32, Int32, AsyncCallback, Object).)
Public method BeginWrite Begins an asynchronous write operation that writes Bytes from the specified buffer to the stream. (Overrides Stream.BeginWrite(Byte[], Int32, Int32, AsyncCallback, Object).)
Public method Close Closes the current stream and releases any resources (such as sockets and file handles) associated with the current stream. (Inherited from Stream.)
Public method CopyTo(Stream) Reads the bytes from the current stream and writes them to the destination stream. (Inherited from Stream.)
Public method CopyTo(Stream, Int32) Reads all the bytes from the current stream and writes them to a destination stream, using a specified buffer size. (Inherited from Stream.)
Public method CreateObjRef Creates an object that contains all the relevant information required to generate a proxy used to communicate with a remote object. (Inherited from MarshalByRefObject.)
Protected method CreateWaitHandle Obsolete. Allocates a WaitHandle object. (Inherited from Stream.)
Public method Dispose() Releases all resources used by the Stream. (Inherited from Stream.)
Protected method Dispose(Boolean) Releases the unmanaged resources used by the SslStream and optionally releases the managed resources. (Overrides AuthenticatedStream.Dispose(Boolean).)
Public method EndAuthenticateAsClient Ends a pending asynchronous server authentication operation started with a previous call to BeginAuthenticateAsServer.
Public method EndAuthenticateAsServer Ends a pending asynchronous client authentication operation started with a previous call to BeginAuthenticateAsClient.
Public method EndRead Ends an asynchronous read operation started with a previous call to BeginRead. (Overrides Stream.EndRead(IAsyncResult).)
Public method EndWrite Ends an asynchronous write operation started with a previous call to BeginWrite. (Overrides Stream.EndWrite(IAsyncResult).)
Public method Equals(Object) Determines whether the specified Object is equal to the current Object. (Inherited from Object.)
Protected method Finalize Allows an object to try to free resources and perform other cleanup operations before it is reclaimed by garbage collection. (Inherited from Object.)
Public method Flush Causes any buffered data to be written to the underlying device. (Overrides Stream.Flush().)
Public method GetHashCode Serves as a hash function for a particular type. (Inherited from Object.)
Public method GetLifetimeService Retrieves the current lifetime service object that controls the lifetime policy for this instance. (Inherited from MarshalByRefObject.)
Public method GetType Gets the Type of the current instance. (Inherited from Object.)
Public method InitializeLifetimeService Obtains a lifetime service object to control the lifetime policy for this instance. (Inherited from MarshalByRefObject.)
Protected method MemberwiseClone() Creates a shallow copy of the current Object. (Inherited from Object.)
Protected method MemberwiseClone(Boolean) Creates a shallow copy of the current MarshalByRefObject object. (Inherited from MarshalByRefObject.)
Protected method ObjectInvariant Infrastructure. Provides support for a Contract. (Inherited from Stream.)
Public method Read Reads data from this stream and stores it in the specified array. (Overrides Stream.Read(Byte[], Int32, Int32).)
Public method ReadByte Reads a byte from the stream and advances the position within the stream by one byte, or returns -1 if at the end of the stream. (Inherited from Stream.)
Public method Seek Infrastructure. Throws a NotSupportedException. (Overrides Stream.Seek(Int64, SeekOrigin).)
Public method SetLength Sets the length of the underlying stream. (Overrides Stream.SetLength(Int64).)
Public method ToString Returns a string that represents the current object. (Inherited from Object.)
Public method Write(Byte[]) Writes the specified data to this stream.
Public method Write(Byte[], Int32, Int32) Write the specified number of Bytes to the underlying stream using the specified buffer and offset. (Overrides Stream.Write(Byte[], Int32, Int32).)
Public method WriteByte Writes a byte to the current position in the stream and advances the position within the stream by one byte. (Inherited from Stream.)
Top

SSL protocols help to provide confidentiality and integrity checking for messages transmitted using an SslStream. An SSL connection, such as that provided by SslStream, should be used when communicating sensitive information between a client and a server. Using an SslStream helps to prevent anyone from reading and tampering with information while it is in transit on the network.

An SslStream instance transmits data using a stream that you supply when creating the SslStream. When you supply this underlying stream, you have the option to specify whether closing the SslStream also closes the underlying stream. Typically, the SslStream class is used with the TcpClient and TcpListener classes. The GetStream method provides a NetworkStream suitable for use with the SslStream class.

After creating an SslStream, the server and optionally, the client must be authenticated. The server must provide an X509 certificate that establishes proof of its identity and can request that the client also do so. Authentication must be performed before transmitting information using an SslStream. Clients initiate authentication using the synchronous AuthenticateAsClient methods, which block until the authentication completes, or the asynchronous BeginAuthenticateAsClient methods, which do not block waiting for the authentication to complete. Servers initiate authentication using the synchronous AuthenticateAsServer or asynchronous BeginAuthenticateAsServer methods. Both client and server must initiate the authentication.

The authentication is handled by the Security Support Provider (SSPI) channel provider. The client is given an opportunity to control validation of the server's certificate by specifying a RemoteCertificateValidationCallback delegate when creating an SslStream. The server can also control validation by supplying a RemoteCertificateValidationCallback delegate. The method referenced by the delegate includes the remote party's certificate and any errors SSPI encountered while validating the certificate. Note that if the server specifies a delegate, the delegate's method is invoked regardless of whether the server requested client authentication. If the server did not request client authentication, the server's delegate method receives a null certificate and an empty array of certificate errors.

If the server requires client authentication, the client must specify one or more certificates for authentication. If the client has more than one certificate, the client can provide a LocalCertificateSelectionCallback delegate to select the correct certificate for the server. The client's certificates must be located in the current user's "My" certificate store. Client authentication via certificates is not supported for the Ssl2 (SSL version 2) protocol.

If the authentication fails, you receive a AuthenticationException, and the SslStream is no longer useable. You should close this object and remove all references to it so that it can be collected by the garbage collector.

When the authentication process, also known as the SSL handshake, succeeds, the identity of the server (and optionally, the client) is established and the SslStream can be used by the client and server to exchange messages. Before sending or receiving information, the client and server should check the security services and levels provided by the SslStream to determine whether the protocol, algorithms, and strengths selected meet their requirements for integrity and confidentiality. If the current settings are not sufficient, the stream should be closed. You can check the security services provided by the SslStream using the IsEncrypted and IsSigned properties. The following table shows the elements that report the cryptographic settings used for authentication, encryption and data signing.

Element

Members

The security protocol used to authenticate the server and, optionally, the client.

The SslProtocol property and the associated SslProtocols enumeration.

The key exchange algorithm.

The KeyExchangeAlgorithm property and the associated ExchangeAlgorithmType enumeration.

The message integrity algorithm.

The HashAlgorithm property and the associated HashAlgorithmType enumeration.

The message confidentiality algorithm.

The CipherAlgorithm property and the associated CipherAlgorithmType enumeration.

The strengths of the selected algorithms.

The KeyExchangeStrength, HashStrength, and CipherStrength properties.

After a successful authentication, you can send data using the synchronous Write or asynchronous BeginWrite methods. You can receive data using the synchronous Read or asynchronous BeginRead methods.

If you specified to the SslStream that the underlying stream should be left open, you are responsible for closing that stream when you are done using it.

Note Note

If the application that creates the SslStream object runs with the credentials of a Normal user, the application will not be able to access certificates installed in the local machine store unless permission has been explicitly given to the user to do so.

SslStream assumes that a timeout along with any other IOException when one is thrown from the inner stream will be treated as fatal by its caller. Reusing a SslStream instance after a timeout will return garbage. An application should Close the SslStream and throw an exception in these cases.

The following code example demonstrates creating an TcpListener that uses the SslStream class to communicate with clients.


using System;
using System.Collections;
using System.Net;
using System.Net.Sockets;
using System.Net.Security;
using System.Security.Authentication;
using System.Text;
using System.Security.Cryptography.X509Certificates;
using System.IO;

namespace Examples.System.Net
{
    public sealed class SslTcpServer 
    {
        static X509Certificate serverCertificate = null;
        // The certificate parameter specifies the name of the file 
        // containing the machine certificate.
        public static void RunServer(string certificate) 
        {
            serverCertificate = X509Certificate.CreateFromCertFile(certificate);
            // Create a TCP/IP (IPv4) socket and listen for incoming connections.
            TcpListener listener = new TcpListener(IPAddress.Any, 8080);    
            listener.Start();
            while (true) 
            {
                Console.WriteLine("Waiting for a client to connect...");
                // Application blocks while waiting for an incoming connection.
                // Type CNTL-C to terminate the server.
                TcpClient client = listener.AcceptTcpClient();
                ProcessClient(client);
            }
        }
        static void ProcessClient (TcpClient client)
        {
            // A client has connected. Create the 
            // SslStream using the client's network stream.
            SslStream sslStream = new SslStream(
                client.GetStream(), false);
            // Authenticate the server but don't require the client to authenticate.
            try 
            {
                sslStream.AuthenticateAsServer(serverCertificate, 
                    false, SslProtocols.Tls, true);
                // Display the properties and settings for the authenticated stream.
                DisplaySecurityLevel(sslStream);
                DisplaySecurityServices(sslStream);
                DisplayCertificateInformation(sslStream);
                DisplayStreamProperties(sslStream);

                // Set timeouts for the read and write to 5 seconds.
                sslStream.ReadTimeout = 5000;
                sslStream.WriteTimeout = 5000;
                // Read a message from the client.   
                Console.WriteLine("Waiting for client message...");
                string messageData = ReadMessage(sslStream);
                Console.WriteLine("Received: {0}", messageData);

                // Write a message to the client.
                byte[] message = Encoding.UTF8.GetBytes("Hello from the server.<EOF>");
                Console.WriteLine("Sending hello message.");
                sslStream.Write(message);
            }
            catch (AuthenticationException e)
            {
                Console.WriteLine("Exception: {0}", e.Message);
                if (e.InnerException != null)
                {
                    Console.WriteLine("Inner exception: {0}", e.InnerException.Message);
                }
                Console.WriteLine ("Authentication failed - closing the connection.");
                sslStream.Close();
                client.Close();
                return;
            }
            finally
            {
                // The client stream will be closed with the sslStream
                // because we specified this behavior when creating
                // the sslStream.
                sslStream.Close();
                client.Close();
            }
        }
        static string ReadMessage(SslStream sslStream)
        {
            // Read the  message sent by the client.
            // The client signals the end of the message using the
            // "<EOF>" marker.
            byte [] buffer = new byte[2048];
            StringBuilder messageData = new StringBuilder();
            int bytes = -1;
            do
            {
                // Read the client's test message.
                bytes = sslStream.Read(buffer, 0, buffer.Length);

                // Use Decoder class to convert from bytes to UTF8
                // in case a character spans two buffers.
                Decoder decoder = Encoding.UTF8.GetDecoder();
                char[] chars = new char[decoder.GetCharCount(buffer,0,bytes)];
                decoder.GetChars(buffer, 0, bytes, chars,0);
                messageData.Append (chars);
                // Check for EOF or an empty message.
                if (messageData.ToString().IndexOf("<EOF>") != -1)
                {
                    break;
                }
            } while (bytes !=0); 

            return messageData.ToString();
        }
         static void DisplaySecurityLevel(SslStream stream)
         {
            Console.WriteLine("Cipher: {0} strength {1}", stream.CipherAlgorithm, stream.CipherStrength);
            Console.WriteLine("Hash: {0} strength {1}", stream.HashAlgorithm, stream.HashStrength);
            Console.WriteLine("Key exchange: {0} strength {1}", stream.KeyExchangeAlgorithm, stream.KeyExchangeStrength);
            Console.WriteLine("Protocol: {0}", stream.SslProtocol);
         }
         static void DisplaySecurityServices(SslStream stream)
         {
            Console.WriteLine("Is authenticated: {0} as server? {1}", stream.IsAuthenticated, stream.IsServer);
            Console.WriteLine("IsSigned: {0}", stream.IsSigned);
            Console.WriteLine("Is Encrypted: {0}", stream.IsEncrypted);
         }
         static void DisplayStreamProperties(SslStream stream)
         {
            Console.WriteLine("Can read: {0}, write {1}", stream.CanRead, stream.CanWrite);
            Console.WriteLine("Can timeout: {0}", stream.CanTimeout);
         }
        static void DisplayCertificateInformation(SslStream stream)
        {
            Console.WriteLine("Certificate revocation list checked: {0}", stream.CheckCertRevocationStatus);

            X509Certificate localCertificate = stream.LocalCertificate;
            if (stream.LocalCertificate != null)
            {
                Console.WriteLine("Local cert was issued to {0} and is valid from {1} until {2}.",
                    localCertificate.Subject,
                    localCertificate.GetEffectiveDateString(),
                    localCertificate.GetExpirationDateString());
             } else
            {
                Console.WriteLine("Local certificate is null.");
            }
            // Display the properties of the client's certificate.
            X509Certificate remoteCertificate = stream.RemoteCertificate;
            if (stream.RemoteCertificate != null)
            {
            Console.WriteLine("Remote cert was issued to {0} and is valid from {1} until {2}.",
                remoteCertificate.Subject,
                remoteCertificate.GetEffectiveDateString(),
                remoteCertificate.GetExpirationDateString());
            } else
            {
                Console.WriteLine("Remote certificate is null.");
            }
        }
        private static void DisplayUsage()
        { 
            Console.WriteLine("To start the server specify:");
            Console.WriteLine("serverSync certificateFile.cer");
            Environment.Exit(1);
        }
        public static int Main(string[] args)
        {
            string certificate = null;
            if (args == null ||args.Length < 1 )
            {
                DisplayUsage();
            }
            certificate = args[0];
            SslTcpServer.RunServer (certificate);
            return 0;
        } 
    }
}


The following code example demonstrates creating a TcpClient that uses the SslStream class to communicate with a server.


using System;
using System.Collections;
using System.Net;
using System.Net.Security;
using System.Net.Sockets;
using System.Security.Authentication;
using System.Text;
using System.Security.Cryptography.X509Certificates;
using System.IO;

namespace Examples.System.Net
{
    public class SslTcpClient 
    {   
        private static Hashtable certificateErrors = new Hashtable();

        // The following method is invoked by the RemoteCertificateValidationDelegate.
        public static bool ValidateServerCertificate(
              object sender,
              X509Certificate certificate,
              X509Chain chain,
              SslPolicyErrors sslPolicyErrors)
        {
           if (sslPolicyErrors == SslPolicyErrors.None)
                return true;

            Console.WriteLine("Certificate error: {0}", sslPolicyErrors);

            // Do not allow this client to communicate with unauthenticated servers.
            return false;
        }
        public static void RunClient(string machineName, string serverName)  
        {
            // Create a TCP/IP client socket.
            // machineName is the host running the server application.
            TcpClient client = new TcpClient(machineName,443);
            Console.WriteLine("Client connected.");
            // Create an SSL stream that will close the client's stream.
            SslStream sslStream = new SslStream(
                client.GetStream(), 
                false, 
                new RemoteCertificateValidationCallback (ValidateServerCertificate), 
                null
                );
            // The server name must match the name on the server certificate.
            try 
            {
                sslStream.AuthenticateAsClient(serverName);
            } 
            catch (AuthenticationException e)
            {
                Console.WriteLine("Exception: {0}", e.Message);
                if (e.InnerException != null)
                {
                    Console.WriteLine("Inner exception: {0}", e.InnerException.Message);
                }
                Console.WriteLine ("Authentication failed - closing the connection.");
                client.Close();
                return;
            }
            // Encode a test message into a byte array.
            // Signal the end of the message using the "<EOF>".
            byte[] messsage = Encoding.UTF8.GetBytes("Hello from the client.<EOF>");
            // Send hello message to the server. 
            sslStream.Write(messsage);
            sslStream.Flush();
            // Read message from the server.
            string serverMessage = ReadMessage(sslStream);
            Console.WriteLine("Server says: {0}", serverMessage);
            // Close the client connection.
            client.Close();
            Console.WriteLine("Client closed.");
        }
        static string ReadMessage(SslStream sslStream)
        {
            // Read the  message sent by the server.
            // The end of the message is signaled using the
            // "<EOF>" marker.
            byte [] buffer = new byte[2048];
            StringBuilder messageData = new StringBuilder();
            int bytes = -1;
            do
            {
                bytes = sslStream.Read(buffer, 0, buffer.Length);

                // Use Decoder class to convert from bytes to UTF8
                // in case a character spans two buffers.
                Decoder decoder = Encoding.UTF8.GetDecoder();
                char[] chars = new char[decoder.GetCharCount(buffer,0,bytes)];
                decoder.GetChars(buffer, 0, bytes, chars,0);
                messageData.Append (chars);
                // Check for EOF.
                if (messageData.ToString().IndexOf("<EOF>") != -1)
                {
                    break;
                }
            } while (bytes != 0); 

            return messageData.ToString();
        }
        private static void DisplayUsage()
        { 
            Console.WriteLine("To start the client specify:");
            Console.WriteLine("clientSync machineName [serverName]");
            Environment.Exit(1);
        }
        public static int Main(string[] args)
        {
            string serverCertificateName = null;
            string machineName = null;
            if (args == null ||args.Length <1 )
            {
                DisplayUsage();
            }
            // User can specify the machine name and server name.
            // Server name must match the name on the server's certificate. 
            machineName = args[0];
            if (args.Length <2 )
            {
                serverCertificateName = machineName;
            }
            else 
            {
                serverCertificateName = args[1];
            }
            SslTcpClient.RunClient (machineName, serverCertificateName);
            return 0;
        }
    }
}
    


.NET Framework

Supported in: 4, 3.5, 3.0, 2.0

.NET Framework Client Profile

Supported in: 4, 3.5 SP1

Windows 7, Windows Vista SP1 or later, Windows XP SP3, Windows XP SP2 x64 Edition, Windows Server 2008 (Server Core not supported), Windows Server 2008 R2 (Server Core supported with SP1 or later), Windows Server 2003 SP2

The .NET Framework does not support all versions of every platform. For a list of the supported versions, see .NET Framework System Requirements.
Any public static (Shared in Visual Basic) members of this type are thread safe. Any instance members are not guaranteed to be thread safe.
Did you find this helpful?
(1500 characters remaining)
Community Content Add
Annotations FAQ
CertificateErrorDescription never called
Is this function never called or is it because the exception itself will give the required error message?
Additional tips
For development purposes, you also need to have a certificate, which can be generated via a self-signing procedure as documented here and install them onto your machine: http://www.reliablesoftware.com/DasBlog/PermaLink,guid,6507b2c6-473e-4ddc-9e66-8a161e5df6e9.aspx
Some additional tools are needed for these steps as described here http://fergitech.com/wordpress/?p=31 and finally you may need to work around server name mismatches as described here: http://social.msdn.microsoft.com/Forums/en-US/netfxnetcom/thread/7e139c53-73a2-440a-8ddb-fc7f7f23b2ba

Additional tips
For development purposes, you also need to have a certificate, which can be generated via a self-signing procedure as documented here and install them onto your machine: http://www.reliablesoftware.com/DasBlog/PermaLink,guid,6507b2c6-473e-4ddc-9e66-8a161e5df6e9.aspx
Some additional tools are needed for these steps as described here http://fergitech.com/wordpress/?p=31 and finally you may need to work around server name mismatches as described here: http://social.msdn.microsoft.com/Forums/en-US/netfxnetcom/thread/7e139c53-73a2-440a-8ddb-fc7f7f23b2ba

Client and Server need to use the same Port number
The sample as provided has the server listening on port 8080 and the client attempting to connect on port 443. The server and client need to use the same port number.