< SSLStream Class Server Example | Main | NegotiateStream Class Server Example >


 

Chapter 2 Part 11:

Managed I/O - Streams, Readers, and Writers

 

 

What do we have in this chapter 2 Part 11?

  1. C++ SSLStream Client Program Example

  2. C# SSLStream Client Program Example

 

 

 

Note: If you want to experience a complete C++ .NET/C++-CLI programming tutorial please jump to Visual C++ .NET programming tutorial.

 

C++ SSLStream Client Example

 

Create a new CLR console application project and you might want to use SslStreamClientCP as the project and solution names.

 

C++ SSLStream Client Example - a new CLR console mode application project creation

 

Add the following code.

 

 

 

 

// SslStreamClientCP.cpp : main project file.

 

#include "stdafx.h"

 

using namespace System;

using namespace System::Collections;

using namespace System::Globalization;

using namespace System::Net;

using namespace System::Net::Security;

using namespace System::Net::Sockets;

using namespace System::Security::Authentication;

using namespace System::Text;

using namespace System::Security::Cryptography::X509Certificates;

using namespace System::IO;

 

public ref class SslTcpClient

{

    private:

        static Hashtable^ certificateErrors = gcnew Hashtable;

        // Load a table of errors that might cause the certificate authentication to fail.

        static void InitializeCertificateErrors()

        {

            certificateErrors->Add(0x800B0101, "The certification has expired.");

            certificateErrors->Add(0x800B0104,

                "A path length constraint in the certification chain has been violated.");

            certificateErrors->Add(0x800B0105,

                "A certificate contains an unknown extension that is marked critical.");

            certificateErrors->Add(0x800B0107,

                "A parent of a given certificate in fact did not issue that child certificate.");

            certificateErrors->Add(0x800B0108,

                "A certificate is missing or has an empty value for a necessary field.");

            certificateErrors->Add(0x800B0109, "The certificate root is not trusted.");

            certificateErrors->Add(0x800B010C, "The certificate has been revoked.");

            certificateErrors->Add(0x800B010F,

                "The name in the certificate does not not match the host name requested by the client.");

            certificateErrors->Add(0x800B0111,

                "The certificate was explicitly marked as untrusted by the user.");

            certificateErrors->Add(0x800B0112,

                "A certification chain processed correctly, but one of the CA certificates is not trusted.");

            certificateErrors->Add(0x800B0113, "The certificate has an invalid policy.");

            certificateErrors->Add(0x800B0114,

                "The certificate name is either not in the permitted list or is explicitly excluded.");

            certificateErrors->Add(0x80092012,

                "The revocation function was unable to check revocation for the certificate.");

            certificateErrors->Add(0x80090327,

                "An unknown error occurred while processing the certificate.");

            certificateErrors->Add(0x80096001, "A system-level error occurred while verifying trust.");

            certificateErrors->Add(0x80096002,

                "The certificate for the signer of the message is invalid or not found.");

            certificateErrors->Add(0x80096003, "One of the counter signatures was invalid.");

            certificateErrors->Add(0x80096004, "The signature of the certificate cannot be verified.");

            certificateErrors->Add(0x80096005,

                "The time stamp signature or certificate could not be verified or is malformed.");

            certificateErrors->Add(0x80096010, "The digital signature of the object was not verified.");

            certificateErrors->Add(0x80096019, "The basic constraint extension of a certificate "

                "has not been observed.");

        }

 

        static String^ CertificateErrorDescription(UInt32 problem)

        {

            // Initialize the error message dictionary if it is not yet available.

            Console::WriteLine("Initializing error message dictionary...");

            if (certificateErrors->Count == 0)

            {

                InitializeCertificateErrors();

            }

 

            String^ description = safe_cast<String^>(certificateErrors[problem]);

            if (description == nullptr)

            {

                description = String::Format(CultureInfo::CurrentCulture,

                    "Unknown certificate error - 0x{0:x8}", problem);

            }

 

            return description;

        }

 

    public:

        // The following method is invoked by the CertificateValidationDelegate.

        static bool ValidateServerCertificate(

            Object^ sender,

            X509Certificate^ certificate,

            X509Chain^ chain,

            SslPolicyErrors sslPolicyErrors)

        {

            Console::WriteLine("Validating the server certificate.");

            if (sslPolicyErrors == SslPolicyErrors::None)

                return true;

 

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

            // Do not allow this client to communicate with unauthenticated servers.

            return false;

        }

 

        static void RunClient(String^ machineName, String^ serverName)

        {

            // Create a TCP/IP client socket. machineName is the host running the server application.

            Console::WriteLine("Creating a TCP/IP client socket...");

            TcpClient^ client = gcnew TcpClient(machineName, 8080);

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

 

            // Create an SSL stream that will close the client's stream.

            Console::WriteLine("Creating an SSL stream that will close the client\'s stream...");

            SslStream^ sslStream = gcnew SslStream(

                client->GetStream(), false,

                gcnew RemoteCertificateValidationCallback(ValidateServerCertificate),

                nullptr);

 

            // The server name must match the name on the server certificate.

            Console::WriteLine("Server name matching...");

            try

            {

                sslStream->AuthenticateAsClient(serverName);

            }

            catch (AuthenticationException^ ex)

            {

                Console::WriteLine("Exception: {0}", ex->Message);

                if (ex->InnerException != nullptr)

                {

                    Console::WriteLine("Inner exception: {0}", ex->InnerException->Message);

                }

 

                Console::WriteLine("Authentication failed - closing the connection.");

                sslStream->Close();

                client->Close();

                return;

            }

            // Encode a test message into a byte array. Signal the end of the message using the "<EOF>".

            Console::WriteLine("Do some encoding...");

            array<Byte>^ messsage = Encoding::UTF8->GetBytes("Client: Hello from the client.<EOF>");

 

            // Send hello message to the server.

            Console::WriteLine("Client: Sending all my client luv to youuuu...");

            sslStream->Write(messsage);

            sslStream->Flush();

            // Read message from the server.

            Console::WriteLine("Client: Reading all my luv message from the server...");

            String^ serverMessage = ReadMessage(sslStream);

            Console::WriteLine("My luv Server says: {0}", serverMessage);

 

            // Close the client connection.

            sslStream->Close();

            client->Close();

            Console::WriteLine("Client ssl stream closed...");

        }

    private:

        static String^ ReadMessage(SslStream^ sslStream)

        {

 

            // Read the  message sent by the server. The end of the message is signaled using the "<EOF>" marker.

            array<Byte>^ buffer = gcnew array<Byte>(2048);

            StringBuilder^ messageData = gcnew StringBuilder;

            // Use Decoder class to convert from bytes to UTF8 in case a character spans two buffers.

            Encoding^ u8 = Encoding::UTF8;

            Decoder^ decoder = u8->GetDecoder();

 

            int bytes = -1;

            do

            {

                bytes = sslStream->Read(buffer, 0, buffer->Length);

                array<__wchar_t>^ chars = gcnew array<__wchar_t>(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();

        }

    };

 

int main(array<System::String ^> ^args)

{

    args = Environment::GetCommandLineArgs();

    String^ serverCertificateName = nullptr;

    String^ machineName = nullptr;

 

    if (args == nullptr || args->Length < 2)

    {

        Console::WriteLine("To start the client specify:");

        Console::WriteLine("{0} machineName [serverName]", args[0]);

        return 1;

    }

 

    // User can specify the machine name and server name. Server name must match the name on the server's certificate.

    machineName = args[1];

    if (args->Length < 3)

    {

        serverCertificateName = machineName;

    }

    else

    {

        serverCertificateName = args[2];

    };

    SslTcpClient::RunClient(machineName, serverCertificateName);

    return 0;

}

 

 

Build and run the project. The following are output examples.

 

C++ SSLStream Client Example - output example

 

Next, let test the server and client program. Firstly, run the server program with valid certificate file and the following just an example that using a sample CER file for Windows Mobile.

 

C++ SSLStream Client Example - output sample in action

 

Then, run the client program.

 

C++ SSLStream Client Example - output sample with exception thrown

 

C++ SSLStream Client Example - an output example with exception thrown

 

Next, we change the port to 443 (SSL port) and rebuild our program. Then we test this client against known SSL server. The following are output examples.

 

t>

C++ SSLStream Client Example - output sample against the https server

 

C++ SSLStream Client Example - output sample with invalid certificate

 

C++ SSLStream Client Example - output sample against the https server validation

 

C# SSLStream Client Example

 

The following code example demonstrates creating a TcpListener that uses the SslStream class to communicate with clients. Create a new console application project. You can use the solution and project name as shown in the following Figure.

 

C# SSLStream Client Example - console application project creation

 

Add the following code.

 

using System;

using System.Collections.Generic;

using System.Text;

using System.Collections;

using System.Net;

using System.Net.Security;

using System.Net.Sockets;

using System.Security.Authentication;

using System.Security.Cryptography.X509Certificates;

using System.IO;

 

namespace SslStreamClientCS

{

    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("executable_name 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;

        }

    }

}

 

 

 

 

An output sample:

 

C# SSLStream Client Example - a client output sample in action

 

The following sample output shows the client and server in action.

 

C# SSLStream Client Example - client and server output sample with thrown exceptions

 


< SSLStream Class Server Example | Main | NegotiateStream Class Server Example >