Using Secure Sockets in .NET

Friday Nov 6th 2015 by Peter Shaw
Share:

Securing regular sockets against tampering isn't that difficult if you follow this lead.

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.

Share:
Home
Mobile Site | Full Site
Copyright 2017 © QuinStreet Inc. All Rights Reserved