Geeks With Blogs
Tex-blog Mobile and other stuff
Below you will find a sample solution for Practice 1 task in first objective of Chapter 12 review. This code is based on SslStream sample code that can be found in this class documentation.

//#define USE_FILE_BASED_CERTIFICATE

 

using System;

using System.Collections.Generic;

using System.Text;

using System.Net;

using System.Net.Sockets;

using System.Security.Cryptography.X509Certificates;

using System.Net.Security;

using System.Security.Authentication;

using System.IO;

using System.Threading;

 

/*

 * Simple SSL command line chat. Its purpose is to show how to setup client/server

 * connection using Secure Socket Layer connection with the SslStream class.

 *

 * To run it you need to create proper certificates, good start is here:

 *  http://blogs.technet.com/jhoward/archive/2005/02/02/365323.aspx

 *

 * Below you will find a crash course on proper certificate setup for

 * development purposes (it was created with the use of above great blog entry):

 *

 * You will need at least two certificates, the so called root certificate

 * (normally those certificates possess organizations like Verisign or Thawte),

 * next you create with it your own certificate that will be used to establish

 * SSL connection. So to create those certificates

 *

 * First call this (use "Open Visual Studio 2005 Command Prompt" to make sure all

 * paths are ok):

 * $ makecert -pe

 *            -n "CN=Test And Dev Root Authority"

 *            -ss my

 *            -sr LocalMachine

 *            -a sha1

 *            -sky signature

 *            -r "Test And Dev Root Authority.cer"

 *

 * Then call this:

 * $ makecert -pe

 *            -n "CN=MachineName"

 *            -ss my

 *            -sr LocalMachine

 *            -a sha1

 *            -sky exchange

 *            -eku 1.3.6.1.5.5.7.3.1

 *            -in "Test And Dev Root Authority"

 *            -is MY

 *            -ir LocalMachine

 *            -sp "Microsoft RSA SChannel Cryptographic Provider"

 *            -sy 12

 *            MachineName.cer

 *

 * Replace MachineName with your machine Full Computer Name (you may use what

 * Environment.MachineName returns).

 *

 * Then open mmc console and look into cerificates extension for your

 * Local Machine, copy "Test And Dev Root Authority" to Trusted Authorities

 * folder. This step is important. Remember that you will have to install

 * 'Test And Dev Root Authority.cer' in Trusted Authorities folder

 * certificate on every computer where you will want to run client. And

 * additionally 'MachineName.cer' on any machine where server will be run.

 *

 * Some of the errors I had to deal with:

 * RemoteCertificateNameMismatch - this was happening due to me writing bad

 *    server name on client side in call to AuthenticateAsClient(serverName).

 *    This name (XXXXX) should be whatever you put in above makecert

 *    command : "CN=XXXXX".

 *

 * RemoteCertificateChainErrors - this error was due to the missing root

 *    certificate, that was used to create sub certificate.

 */

namespace SSLChat

{

  class Program

  {

 

#if (USE_FILE_BASED_CERTIFICATE)

    static X509Certificate certFile = null;

#else

    static X509Certificate2 certFile = null;

#endif

 

    //

    // Server code

 

    public static void StartAsServer()

    {

#if (USE_FILE_BASED_CERTIFICATE)

      string cfile = Environment.MachineName + ".cer";

      string certFilePath = @"C:\Projects\536\Samples\SSLSample\" + cfile;

      if (!File.Exists(certFilePath))

        certFilePath = cfile;

      certFile = new X509Certificate(certFilePath);

#else

      X509Store store = new X509Store(StoreName.My,

        StoreLocation.LocalMachine);

      store.Open(OpenFlags.ReadOnly);

      X509Certificate2Collection certs =

        store.Certificates.Find(X509FindType.FindBySubjectName,

        Environment.MachineName, false);

      certFile = certs[0];

      store.Close();

#endif

      Console.WriteLine("Certificate subject: {0}", certFile.Subject);

      TcpListener listener = new TcpListener(IPAddress.Any, 8088);

      listener.Start();

      System.Console.WriteLine(

        "Waiting for client on port 8088... (use Ctrl+C to stop)");

      TcpClient tcpClient = listener.AcceptTcpClient();

      ProcessClient(tcpClient);

    }

   

    public static void ProcessClient(TcpClient client)

    {

      SslStream ssls = new SslStream(client.GetStream(), false);     

      try

      {

        ssls.AuthenticateAsServer(certFile, false,

          SslProtocols.Tls, true);       

        ssls.ReadTimeout = 500000;

        ssls.WriteTimeout = 500000;

        StartReaderSenderThreads(ssls);       

      }

      catch (AuthenticationException ex)

      {

        System.Console.WriteLine(ex.Message);

        System.Console.WriteLine(ex.InnerException.Message);

      }

      finally {

        ssls.Close();

        client.Close();

      }

    }

 

    //

    // Client code

 

    // 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 StartAsClient(String server, string serverName, int port)

    {

      TcpClient client = new TcpClient(server, port);

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

      SslStream ssls = new SslStream(client.GetStream(), false,

        new RemoteCertificateValidationCallback(ValidateServerCertificate),

        null);

      try

      {

        ssls.AuthenticateAsClient(serverName);

        StartReaderSenderThreads(ssls);

      }

      catch (AuthenticationException e)

      {

        Console.WriteLine(e.Message);

        Console.WriteLine(e.InnerException.Message);

      }

      finally

      {

        //ssls.Close();

        client.Close();

      }    

    }

 

    //

    // Common methods

 

    static Thread readerThread = null;

    static Thread senderThread = null;

    public static void ReaderThread(Object str)

    {

      SslStream ssls = str as SslStream;

      while (true)

      {

        string message = ReadMessage(ssls);

        System.Console.WriteLine("Remote: " + message);

        if (message.ToLower().Equals("\\quit"))

        {

          ssls.Write(Encoding.UTF8.GetBytes("\\quit<EOF>"));

          ssls.Flush();

          break;

        }

      }

    }

 

    public static void SenderThread(Object str)

    {

      SslStream ssls = str as SslStream;

      while (true)

      {

        string message = Console.ReadLine();

        if (readerThread.IsAlive)

        {

          ssls.Write(Encoding.UTF8.GetBytes(message + "<EOF>"));

          ssls.Flush();

        }

        if (message.ToLower().Equals("\\quit"))

          break;

      }

    }

 

    public static void StartReaderSenderThreads(SslStream ssls)

    {

      ParameterizedThreadStart readerStarter =

        new ParameterizedThreadStart(ReaderThread);

      ParameterizedThreadStart senderStarter =

        new ParameterizedThreadStart(SenderThread);

      readerThread = new Thread(readerStarter);

      senderThread = new Thread(senderStarter);

      readerThread.Start(ssls);

      senderThread.Start(ssls);      

      readerThread.Join();

      senderThread.Join();

    }

 

    public static string ReadMessage(SslStream stream)

    {

      byte[] buffer = new byte[2048];

      StringBuilder sb = new StringBuilder();

      int bytes = -1;

      do {

        bytes = stream.Read(buffer, 0, buffer.Length);

        Decoder decoder = Encoding.UTF8.GetDecoder();

        char[] chars = new char[bytes];

        decoder.GetChars(buffer, 0, bytes, chars, 0);

        sb.Append(chars);

        if (sb.ToString().IndexOf("<EOF>") != -1)

        {

          sb.Replace("<EOF>", "");

          break;

        }

      } while(bytes > 0);

 

      return sb.ToString();

    }

 

    //

    // Main method

 

    static void Main(string[] args)

    {

      if (args.Length != 4 && args.Length != 1 ||

            args[0].ToLower().Equals("-h"))

      {

        Console.WriteLine(

         "Required format:\n" +

         "  [-h|-c|-s] [ServerName] [ServerMachine] [ServerPort]\n" +

         "  Use -h to display this help screen\n" +

         "  Use -s with no arguments to start as a server\n" +

         "  Use -c with ServerName, ServerMachine and ServerPort arguments\n" +

         "   to start as a client\n\n" +

         "  For example: \n\n" +

         "  C:\\SSLChat.exe -s\n" +

         "    will start a server\n\n" +

         "  C:\\SSLChat.exe -c localhost MachineName 8088\n" +

         "    will start a client, and connect to server\n" +

         "    located on localhost whose name is MachineName and\n" +

         "    which is listening on port 8088\n\n" +

         "  Use \\quit on command line to exit session\n");

        Environment.Exit(-1);

      }

      if (args[0].ToLower().Equals("-c"))

        StartAsClient(args[1], args[2], Convert.ToInt32(args[3]));

      if (args[0].ToLower().Equals("-s"))

        StartAsServer();

    }

  }

}

Posted on Monday, October 1, 2007 5:25 PM 70-536 preparation | Back to top


Comments on this post: SSLChat: simple presentation of .NET 2.0 SslStream class usage (Ch12)

# re: SSLChat: simple presentation of .NET 2.0 SslStream class usage (Ch12)
Requesting Gravatar...
Hi,

Thanks for the simple SSLStream example. When I run it on a single machine, it works very well, but when I run the server on one machine, and the client on the other, connecting across the Internet, I receive a "RemoteCertificateNameMismatch" error.

I believe that I installed the certificates properly on both the server and the client. On the server side, I just did as you have instructed. On the client side, I put the 'Test And Dev Root Authority.cer' certificate into the trusted root (both the Personal and Local Machine roots, just to make sure), yet the client fails with that error.

Have you encountered this error? Does it sound like I am doing things correctly?

Thanks a lot!

Philipp
Left by Philipp on Feb 24, 2008 11:08 PM

# re: SSLChat: simple presentation of .NET 2.0 SslStream class usage (Ch12)
Requesting Gravatar...
Have you checked that you are using proper server machine name on your client? This is the third parameter and it should be equal to what 'ipconfig /all' command returns at the very top. This is the cause of RemoteCertificateNameMismatch on my machine.

The cause might be also due to different network configurations, but I am not sure about it.
Left by Martin on Feb 25, 2008 12:25 PM

# re: SSLChat: simple presentation of .NET 2.0 SslStream class usage (Ch12)
Requesting Gravatar...
RemoteCertificateChainErrors - this error was due to the missing root

* certificate, that was used to create sub certificate

I am getting this error, any suggestion?

Thanks a lot
-Viji
Left by Viji on Mar 07, 2008 2:52 PM

# re: SSLChat: simple presentation of .NET 2.0 SslStream class usage (Ch12)
Requesting Gravatar...
I am not sure what is the problem, but I am not an expert in this field:-) To install certificate on client machine I follow these steps:
1. On server I choose in Certificates MMC console (for Local Machine), "Test And Dev Root Authority" certificate from "Trusted Authorities folder".
2. I open popup menu and then export it to .cer file.
3. On client I right click on .cer file and choose 'install certificate' from popup menu. I install it to default directory.

This assumes proper instalation of root and sub certificate on server machine.

And thats all, then I just start server first, and then client - and this works.

I have checked how this code works over internet connection, I connected to internet on my loptop with cell phone and used my local ADSL static IP as a server. I have not done any changes to certificates configuration. The only thing I had to do was to open 8088 port on my router - and direct all trafic on that port to my local IP address.

Hope that helps.
Left by Martin on Mar 10, 2008 3:40 PM

Your comment:
 (will show your gravatar)
 


Copyright © Martinez | Powered by: GeeksWithBlogs.net | Join free