Using Secure Sockets in .NET

With all the talk of HTTPS and encrypted Web page communications these days, you could be forgiven for thinking that we don't need anything else to protect our data. Many developers, however, don't stop to think about security in the back end of the application they're working on.

Many applications need to make requests to other systems and services, and one of the most effective ways is to use raw sockets. Securing regular sockets against tampering is a little bit more challenging than making a HTTPS request, but, once you understand what's needed, it's not difficult.

To get started, we first need a couple of applications that implement a TCP Client and server. You'll need two projects: one for the client and one for the server. Fire up two copies of Visual Studio and create two separate console mode application projects. We'll create a simple server first, and then we'll create a client.

First, however, you'll need a certificate to use. For this, you'll need to use the Visual Studio command prompt (the makecert tool is not available using a normal cmd prompt). Head to your Visual Studio tools in your Start menu, and open up a developer tools prompt.

Sock1
Figure 1: The VS command prompt is in Visual Studio Tools

Once you have the command prompt open, type the following command

Makecert -r -pe -n "CN=MySslSocketCertificate"
                -b 01/01/2015
                -e 01/01/2025
                -sk exchange
                -ss my

This will create you a self-signed certificate called "MySslSocketCertificate" in your personal (My) Windows certificate store. If you want to know what all the parameters mean, type:

Makecert -! and Makecert -?

at your Visual Studio command tools prompt.

If you open the MMC tool and load your certificate snap-in, you should see your new certificate listed there.

Sock2
Figure 2: SSL Certificate in our cert store

Once you have a certificate in your store, you then can begin to use it in your application, quite easily. We'll start by writing a server application to use this cert, so fire up Visual Studio, and create yourself a console application. We'll call this one "SSLServer".

Open "program.cs" and make sure you have the following using's at the top of the file.

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

After you do that, make sure your main method looks as follows:

   static void Main()
   {
      var serverCertificate = getServerCert();

      var listener = new TcpListener(IPAddress.Any,
         _listeningPort);
      listener.Start();

      while (true)
      {
         using (var client = listener.AcceptTcpClient())
         using (var sslStream = new SslStream(client.GetStream(),
            false, ValidateCertificate))
         {
            sslStream.AuthenticateAsServer(serverCertificate,
               true, SslProtocols.Tls12, false);

            var inputBuffer = new byte[4096];
            var inputBytes = 0;
            while (inputBytes == 0)
            {
               inputBytes = sslStream.Read(inputBuffer, 0,
                  inputBuffer.Length);
            }
            var inputMessage = Encoding.UTF8.GetString(inputBuffer,
               0, inputBytes);
            Console.WriteLine("GOT Data: {0}", inputMessage);

         }
      }
   }

You'll also need a global var called "_listeningPort"

private static int _listeningPort = 2000;

The next part of the puzzle is a handler to validate the used certificates validity. You use this to check for things like expired date ranges, certificate validity, issuer validity, and so forth. However, because we're using self-signed certificates, the .NET runtime will find just about every reason it can to complain about the certificate, so for this sample we'rere just going to return 'true'

I hopefully don't have to remind you that you shouldn't be deploying code that does this in production, so please make sure that if you use this, you buy and register a properly registered certificate.

The code to handle the validation is as follows:

   static bool ValidateCertificate(Object sender,
      X509Certificate certificate, X509Chain chain,
      SslPolicyErrors sslPolicyErrors)
   {
      // For this sample under Windows 7 I also get
      // a remote cert not available error, so we
      // just do a return true here to signal that
      // we are trusting things. In the real world,
      // this would be very bad practice.
      return true;
      if (sslPolicyErrors == SslPolicyErrors.None)
         { return true; }
      // we don't have a proper certificate tree
      if (sslPolicyErrors ==
            SslPolicyErrors.RemoteCertificateChainErrors)
            { return true; }
         return false;
   }

As you can see, I've added a bit of code after the return to show you how to test for various things. You'll need one more part to load the certificate file; we'll get to that in a moment.

Next, we turn our attention to the client. This is just as simple as the server. Start another copy of Visual Studio, and a new console mode app.

Add the following usings to your 'program.cs' file:

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

and the following global vars to the 'program' class:

private static int _hostPort = 2000;
private static string _hostName = "localhost";
private static string ServerCertificateName =
   "MySslSocketCertificate";

Note that it's important you get the server certificate name correct here. Even though your certificate might have "CN=xxxxx", you must make sure that you don't include the "CN=" part when trying to validate.

Continuing with the client, make sure your main routine in program.cs looks as follows:

   static void Main()
   {
      var clientCertificate = getServerCert();
      var clientCertificateCollection = new
         X509CertificateCollection(new X509Certificate[]
         { clientCertificate });

      using (var client = new TcpClient(_hostName, _hostPort))
      using (var sslStream = new SslStream(client.GetStream(),
         false, ValidateCertificate))
      {
         sslStream.AuthenticateAsClient(ServerCertificateName,
            clientCertificateCollection, SslProtocols.Tls12, false);

         var outputMessage = "I haz secure data";
         var outputBuffer = Encoding.UTF8.GetBytes(outputMessage);
         sslStream.Write(outputBuffer);
         Console.WriteLine("Sent: {0}", outputMessage);
      }
   }

As with the server, you'll need a certificate validation routine:

   static bool ValidateCertificate(Object sender,
      X509Certificate certificate, X509Chain chain,
      SslPolicyErrors sslPolicyErrors)
   {
      if (sslPolicyErrors == SslPolicyErrors.None)
         { return true; }
      // ignore chain errors as where self signed
      if (sslPolicyErrors ==
         SslPolicyErrors.RemoteCertificateChainErrors)
         { return true; }
      return false;
   }

As you can see, the validation routine is pretty much the same as the server one.

The final bit of the puzzle is the get certificate routine. There are a number of ways you can get a certificate into your app, but because we've created one in our personal store using makecert, we'll use the following to find and load our certificate. The same code will work for both the server and the client.

   private static X509Certificate getServerCert()
   {
      X509Store store = new X509Store(StoreName.My,
         StoreLocation.CurrentUser);
      store.Open(OpenFlags.ReadOnly);

      X509Certificate2 foundCertificate = null;
      foreach (X509Certificate2 currentCertificate
         in store.Certificates)
      {
         if (currentCertificate.IssuerName.Name
            != null && currentCertificate.IssuerName.
            Name.Equals("CN=MySslSocketCertificate"))
         {
            foundCertificate = currentCertificate;
            break;
         }
      }


      return foundCertificate;
   }

And that's it. If you run the server, and then run the client, you should see the client run, then quit, and the server report the data sent to it.

Got a .NET bug that's bugging you? Come hunt me down on Twitter as @shawty_ds and let me know; I'll likely write a Nuts & Bolts post about it.



About the Author

Peter Shaw

As an early adopter of IT back in the late 1970s to early 1980s, I started out with a humble little 1KB Sinclair ZX81 home computer. Within a very short space of time, this small 1KB machine became a 16KB Tandy TRS-80, followed by an Acorn Electron and, eventually, after going through many other different machines, a 4MB, ARM-powered Acorn A5000. After leaving school and getting involved with DOS-based PCs, I went on to train in many different disciplines in the computer networking and communications industries. After returning to university in the mid-1990s and gaining a Bachelor of Science in Computing for Industry, I now run my own consulting business in the northeast of England called Digital Solutions Computer Software, Ltd. I advise clients at both a hardware and software level in many different IT disciplines, covering a wide range of domain-specific knowledge—from mobile communications and networks right through to geographic information systems and banking and finance.

Related Articles

Comments

  • There are no comments yet. Be the first to comment!

Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

Most Popular Programming Stories

More for Developers

RSS Feeds

Thanks for your registration, follow us on our social networks to keep up-to-date