Digi Homepage Making Device Networking Easy.
 

SSL Client Sample using Microsoft's Platform SDK

Introduction

This sample demonstrates how to write a SSL client application using the Windows Socket Architecture (WSA) and the Microsoft Platform SDK to communicate with a device server.

For the purpose of this demonstration, the client application initiates communication with the device server by first creating a secure networking socket. Then using that socket, establishes a TCP connection to a single port on the device server and attempts to transmit and receive data from the device server (similar to a loopback test).

Device Server Setup

This sample was designed to operate with the first port on the device server, and requires that the port be configured to listen for and accept secure raw socket connections. After the device server is configured properly, the port must also have a loopback plug attached to it.

Configure the device server
  1. Access the web interface by entering the module’s IP address in a browser’s URL window.
  2. Choose Serial Ports from the Configuration menu.
  3. Configure the module to function as a TCP server by doing the following:
    1. Click the TCP tab.
    2. Check Enable TCP Server.
    3. Specify 2601 as the Raw TCP port.
    4. Click Save.
  4. Connect the loopback plug to port 1.

Client and Server Certificates

An integral part of every SSL communication is the generation and management of client and server X.509 certificates.  These certificates are used for identification and authentication of the certificate holder.  Certificates establish trust using digital signatures with Public-Private Key (PPK) cryptography.  A users identifying information is contained in the certificate and digitally signed by a Certificate Authority (CA) using its own X.509 certificate.  A CA's certificate may also be signed by a CA above it.  This establishes a certificate path.  As long as the top level CA is trusted every CA and certificate below it can be trusted.  A certificate store is used to manage certificates and is implemented in most browsers including Microsoft Internet Explorer --> click on Tools->Internet Options...->Content->Certificates...
There is also the Microsoft Management Console (MMC) which can be used to view and manage certificates.  Launch MMC by clicking Start->Run->type "mmc" and click "OK".  To view certificates within MMC click on Console->Add/Remove Snap-in...->Add->Certificates->Add->Finish.

Server Certificates

This sample requires a server certificate.  For Digi device servers with SSL this certificate is generated the first time the device is powered up.  This certificate will usually be self-issued (self-signed), however some implementations may use server certificates that establish a valid path to a trusted Certificate Authority (CA).  In the case of self-issued server certificates this SSL client sample will display an error (CERT_E_UNTRUSTEDROOT) but will continue as if the connection were secure.  This is only to make it easier to use the sample; a true SSL client application should allow the user to view the certificate and install it if the identity of the certificate holder is verified.  Microsoft Internet Explorer is one such application.  It can be used to install the server certificate and eliminate the error in this sample application.  Follow these steps to install your device server's certificate as a trusted certificate:

  1. Open Internet Explorer.
  2. Use secure http (https) to connect to the device (e.g. https://192.168.x.x).
  3. IE will display a Security Alert.  Click on "View Certificate".
  4. Click on "Install Certificate" and accept the default options in the wizard that follows.
  5. Click "Yes" when prompted to ADD the certificate to the Root Store.
  6. Run the SSL client application and verify that the CERT_E_UNTRUSTEDROOT error no longer occurs.

Client Certificates

A client certificate is only necessary if the server requests it.  Currently, Digi device servers have not implemented client validation so this sample application will not require a client certificate unless the -u option is specified. (Do not use the -u option unless you would like to experiment with client certificates). Once implemented, you will need a third party program to generate client certificates.  Two such programs are OpenSSL and Microsoft Certificate Server.

How To Build

This sample has been and tested with Microsoft Visual C++® 6.0 and Microsoft Platform SDK (February 2003). It has also been tested with Microsoft Platform SDK (July 200). It contains a Developer Studio project file (.dsp), that can be opened in the development environment for editing and compiling.

Visit http://www.microsoft.com/msdownload/platformsdk/sdkupdate/ for the latest information on the Platform SDK.  When using the SDK update site, be sure to download Internet Development SDK as well as the Core SDK. 

To build this sample from within Microsoft Visual C++® 6.0
  1. Select File > Open Workspace... from the main menu.
  2. Change Files of type: to Projects (.dsp).
  3. Locate and open the .dsp file for this sample.
  4. Select Build > Rebuild All menu items to compile the sample.

Running the Client Application

From the command line, navigate to the directory where the executable was built and type the command with no arguments to see a usage message.  In most cases you will just need to type:

WebClient.exe -s192.168.x.x

...where 192.168.x.x is the IP address or dns name of your device server with a loopback plug in port 1.

Step-by-Step

1.  Initialize Windows Sockets

The following initialization code is found in the program's main function.

if(WSAStartup(0x0101, &WsaData) == SOCKET_ERROR)
{
   printf("Error %d returned by WSAStartup\n", GetLastError());
   goto cleanup;
}

Initializing Windows Sockets is critical. Failing to initialize Windows Sockets will cause all other socket function calls to fail.

This sample utilizes Windows Sockets version 1.1. Using newer versions would require modifications to the code. An application that calls WSAStartup must call WSACleanup when it is done using the Windows Socket services (see step 10).

2.  Get a client certificate if needed

If a user is specified try to find a client certificate in the "MY" store.  A valid client certificate must be installed on the client machine:

hMyCertStore = CertOpenSystemStore(0, "MY");
   .
   .
   .
pCertContext = CertFindCertificateInStore(hMyCertStore,
                  X509_ASN_ENCODING,
                  0,
                  CERT_FIND_SUBJECT_STR_A,
                  pszUserName,
                  NULL);

To view the certificates on your system open Microsoft Internet Explorer and select Tools->Internet Options->Content->Certificate

If a user is not specified then just return an empty client credential.  A client certificate is only necessary if the server requests it.

3.  Create a local socket

The call to create a socket looks like this:

Socket = socket(PF_INET, SOCK_STREAM, 0);

4.  Connect to a local port

To connect the socket to a local port, call the following function:

connect(Socket, (struct sockaddr *)&sin, sizeof(sin))

When calling connect, the system takes care of binding the socket.

The sin parameter is the SOCKADDR representation of the local name to bind to. This name consists of an address family (for TCP/IP, always AF_INET), a host address, and a port number.

In this sample, the calls to socket and connect are both done in the function ConnectToServer.

5.  Perform the SSL handshake

The InitializeSecurityContextA funtion is used to handle the required messages and generate a security token that the client will provide to the server:

scRet = g_pSSPI->InitializeSecurityContextA(phCreds,
                    phContext,
                    NULL,
                    dwSSPIFlags,
                    0,
                    SECURITY_NATIVE_DREP,
                    &InBuffer,
                    0,
                    NULL,
                    &OutBuffer,
                    &dwSSPIOutFlags,
                    &tsExpiry);

Client messages are sent using the send function.  Responses are then read using the recv function:

cbData = send(Socket,
            OutBuffers[0].pvBuffer,
            OutBuffers[0].cbBuffer,
            0);
   .
   .
   .
cbData = recv(Socket,
            IoBuffer + cbIoBuffer,
            IO_BUFFER_SIZE - cbIoBuffer,
            0);

These calls are done in the functions PerformClientHandshake and ClientHandshakeLoop.

6.  Validate the server certificate

Although the client certificate is optional, the server certificate is required.  However, if the certificate is not trusted, this sample prints an error message but allows the communication to continue.  A real application would terminate the connection.  See the section above titled "Client and Server Certificates" for information on how to grant trust to the server certificate and avoid the error message.
Get the server's certificate with this function:

Status = g_pSSPI->QueryContextAttributes(&hContext,
                     SECPKG_ATTR_REMOTE_CERT_CONTEXT,
                     (PVOID)&pRemoteCertContext);

Then validate the returned certificate using the VerifyServerCertificate function:

CertVerifyCertificateChainPolicy(CERT_CHAIN_POLICY_SSL,
   pChainContext,
   &PolicyPara,
   &PolicyStatus))

These calls are made in main.

7.  Encrypt and transmit data

This sample sends an HTTP request that, when connected to a web server, would return the specified file.   However, with a loopback plug inserted on the device server the text of the HTTP request will simply be echoed back to the client program.  The data is assembled and then encrypted:

scRet = g_pSSPI->EncryptMessage(phContext,
                    0,
                    &Message,
                    0);

The encrypted data is then sent to the server using send:

cbData = send(Socket,
            pbIoBuffer,
            Buffers[0].cbBuffer +
            Buffers[1].cbBuffer +
            Buffers[2].cbBuffer,
            0);

The Buffers array is copied into a member of the Message struct before these functions are called. This code is found in the HttpsGetFile function.

8.  Receive and decrypt data

To retrieve data received at the local address, call recv.

cbData = recv(Socket,
            pbIoBuffer + cbIoBuffer,
            cbIoBufferLength - cbIoBuffer,
            0);

The data is then decrypted:

scRet = g_pSSPI->DecryptMessage(phContext,
                    &Message,
                    0,
                    NULL);

A Hex dump of the sent and received data is displayed throughout each step of the communication with calls to the function PrintHexDump.

9.  Disconnect from the server and close the socket

Send a close notify to the server using InitializeSecurityContextA and send then close the socket:

closesocket(Socket);

See the function DisconnectFromServer for this step.

10.  Cleanup

Before exiting the program free up memory and handles and call:

WSACleanup();

And close the certificate store:

CertCloseStore(hMyCertStore, 0);

File List

WebClient.c
WebClient.dsp

Keywords

WSAStartup; socket; connect; CertOpenSystemStore; CertFindCertificateInStore; InitializeSecurityContextA; send; recv; QueryContextAttributes; CertVerifyCertificateChainPolicy; closesocket; WSACleanup
 
 
Digi International Inc. 11001 Bren Road E. Minnetonka, MN 55343
PH: (952) 912-3444 or 877-912-3444
FX: (952) 912-4952