Dr. Dobb's is part of the Informa Tech Division of Informa PLC

This site is operated by a business or businesses owned by Informa PLC and all copyright resides with them. Informa PLC's registered office is 5 Howick Place, London SW1P 1WG. Registered in England and Wales. Number 8860726.


Channels ▼
RSS

Implementing an SSL/TLS-Enabled Client/Server on Windows Using GSS API


HexWeb HTML

In recent years, with the advent of the Internet, secure network communication has become increasingly important and is now present in almost every aspect of the network data exchange. One of the most widely used protocols in that area is Secure Sockets Layer (SSL)[1], which is being gradually replaced by its successor -- Transport Layer Security (TLS)[2]. Even though these protocols were specifically designed for the World Wide Web, their utilization is not limited to just the Web traffic -- virtually any type of data sent through any type of network can be secured. Having said that, there are protection goals that define what the secure data means in each case. SSL and TLS provide confidentiality (the data is kept secret from the third party that is not involved in the data exchange), message integrity (the message received is the message sent), and endpoint authentication (one of the communication endpoints or both are verifiably known). If you are working on a project that requires implementation of secure communication channel and that channel must provide the above-mentioned types of data protection, selecting SSL or TLS gives the advantage of a well-documented and well-tested, broadly accepted, and robust protocol.

Fortunately for Windows developers, Microsoft has provided a version of Generic Security Service Application Program Interface (GSS-API) [10]-- called Security Support Provider Interface (SSPI) [5]. SSPI greatly simplifies the daunting task of digging into SSL internals. Using SSPI slashes the development time threefold since the API effectively shields you from needing to know the details about SSL [3]. All that is left is implementing the underlying transport functionality that supplies the data and the logic to wrap calls to SSPI. And best of all, needed C-libraries and header files are part of the Platform SDK that can be freely downloaded from the Microsoft web site. This article describes how to implement an SSL/TLS client/server system using SSPI. The material in this article requires the reader's familiarity with cryptography, SSL/TLS specifications, and X.509 security certificates.

SSPI is a set of generic functions that can be used to access a specific security provider. Providers are either installed as part of the operating system (NTLM, Kerberos) or added later (Microsoft RSA SChannel provider, Microsoft Exchange provider, etc.) by installing other software. In this article, I will be working with the RSA SChannel provider [5] to implement the SSL/TLS functionality.

SChannel is the Microsoft implementation of the GSS API that wraps the SSL/TLS protocol. In the following paragraphs I'll try to explain in detail how to use SChannel and how it works, but first I'd like to stress a few points about advantages of utilizing SChannel:

  • Doing so will free the developer from implementing every aspect of the SSL protocol: the gory details are shielded from the developer by the SSPI.
  • No extra setup is required to run the final application: SChannel is an integral part of the operating system (slight "tweaking" is needed for Win9x family but who runs servers on these systems anyway?). See the next paragraph for the system requirements.
  • SChannel calls follow GSS API standards. There are, of course, some alternatives -- OpenSSL for example. This package is a complete and thorough implementation of the protocol and for someone all too familiar with UNIX is undoubtedly the best choice. The package originally targeted the UNIX community and to compile it relies on the Perl runtime, so some learning curve is required for Windows developers who never worked with UNIX-type systems.

On Windows ME/2000/XP platforms, SChannel is installed and configured by default and does not need additional tuning. For Windows 95/98 OSs a few extra setting up steps are required to ensure that the application you are developing will run smoothly. First, W2K Active Directory client must be installed. The client, dsclient.exe, can be found on the Windows 2000 server CD in the Clients\Win9x directory. After the client is installed, a registry entry must be added to the following key HKLM\System\CurrentControlSet\ Control\SecurityProviders\ SecurityProviders. Simply append the string:

, schannel.dll
to the existing REG_SZ value. Windows NT machines will require at least Option Pack 4.0.

Provided freely with the article is a project demonstrating implementation of a HTTP server with SSL/TLS capabilities. This server utilizes SSPI wrapper classes -- CSslProvider, CSslCredentials, CSslTransport, and CSspiLib. Listings 1, 2, and 3 (Listing 3 available for download at <www.cuj.com/code>), respectively, show partial definition and implementation files for the first three classes. The design goal here was to simplify access to the functionality provided by SChannel. Implementing the handshake or the data exchange phase of the secure protocol with the SSPI can be tedious because of the number of error codes returned and the interpretation of input/output buffer types used. By utilizing the wrappers, your application eliminates the need to keep track of the data flow inside the secure channel, thus you can concentrate on providing the plain or encrypted data. In contrast to the SSPI functions, the wrapper classes have two types of return codes -- success (S_OK) and failure. This greatly simplifies the decision-making process if any type of error occurs; receiving a failure code should lead to the socket closure, or whatever the transport you are using to receive or send the data.

To deliver the secure context and the encrypted or plain data to the SChannel, I'm providing the CSslTransport class. This class can only be obtained through a call to the CSslProvider's function ObtainTransport, which ensures that the transport object during its construction receives a reference to the CSslCredentials object. The latter in turn provides security credentials during the negotiation phase of the SSL protocol. The sequence of getting the wrappers to do the work required is as follows:

    1. Your application calls CSspiLib::Load() to prepare the PSecurityFunctionTable (see the topic below);
    2. Next, the application calls one of the overloaded Init() functions of CSslProvider to obtain security credentials, used later in the negotiation phase;
    3. Once the credentials are obtained, any number of the CSslTransport instances can be spawned by calling the ObtainTransport() function. When the application is done using the transport object, it should call its Destroy() method (see the CAsyncXferSocket class for details);
    4. Shutting down the application will require calling CSslProvider's Destroy() function to release the credentials obtained earlier;
    5. The last step is releasing the hold on the SSPI DLL by calling CSspiLib::Unload().

In the case of the demo HTTP server, a descendant of the MFC's CAsyncSocket class safeguards the transport object. The SSL transport class fires events to control the inbound and outbound data flow. There are three types of events -- Inbound Data Available, Outbound Data Available, and Disconnect. See the CAsyncXferSocket class for implementation details and for an example how this class utilizes the new unified event model provided in Visual C++ v7.0. Because of the aforementioned events, to compile and run the project, you'll need Visual C++ v7.0, which is available as part of the VS.NET suite. If you don't have the new compiler, you'll have to modify the code to provide old-fashion callback notification functions similar to these three events fired by the CSslTransport object. Note also that the wrapper classes are independent of the MFC and can be plugged into any environment.

SChannel at Work

Before describing in detail the functionality wrapped by the classes mentioned above, I would encourage those readers who are novices to the topic to study the SSL/TLS specifications. Recently a lot of good books on the issue have appeared on the bookstore shelves. If you are serious about developing an SSL-aware application, a book would be a good investment [8], [9].

The first step in taking advantage of the SChannel functionality is loading the SSPI dynamic link library (DLL) and setting up a special table, PSecurityFunctionTable, with valid SSPI function pointers. The DLL's name differs depending on the operating system. Windows NT systems have security.dll, exposing the SSPI functionality; all other Microsoft systems use secur32.dll.

You can download the function table by calling a little nifty procedure, which the SSPI DLL exports as InitSecurityInterface. The export is available in two flavors: ANSI and UNICODE, with the letters A and W at the end, respectively, identifying the flavor. To simplify, you may consider using a SECURITY_ENTRYPOINT string defined in sspi.h, which will map to the correct entry point name in either ANSI or UNICODE environments. The CSspiLib class, provided in the supplemental code accompanying this article, wraps DLL initialization and function calls to SChannel using a set of static functions with the same names and parameters that are defined by the SSPI. This class takes care of loading the correct DLL and initializing the function table.

Before SChannel can assist in creating a secure channel, it must tag us with the Windows-type handle: its credentials. This handle is then passed around to other functions, so SChannel knows who is requesting the service and thus does not get confused in servicing multiple users. The handle will also be associated with any special attributes that the principal initiating a session possesses, an X.509 certificate for instance. (Microsoft prefers to use the term 'principal' to identify the entity on behalf of which calls to SChannel functions are made. I'll be using the same term.) For obtaining the handle SSPI provides the function named AcquireCredentialsHandle(). A lot of references to structures, function, and error codes mentioned in this article can be found in [5] under the SChannel section.

Multiple overrides of the CSslProvider Init() function in the sample code provide shortcuts to the function that acquires SChannel credentials. There, I'm simplifying the call to AcquireCredentialsHandle() with fewer parameters (see Listing 1 for details), which is sufficient enough for most of the cases. If the application you are developing requires a special treatment, you'll need to acquire the credentials yourself and then supply them in one of the Init() overrides.

The original AcquireCredentialsHandle() takes a lot of parameters (remember that this is a generic function, which is used by other types of security providers) but to initialize SChannel credentials only four of the parameters are relevant. Although "the fifth element," ptsExpiry, can optionally be used, SChannel always returns zero in it. I won't be wasting the reader's time describing each parameter except for one: the SCHANNEL_CRED structure. This is a rather important fellow through whom SChannel receives everything it needs to know about the principal, so let's scrutinize it.

SCHANNEL_CRED Structure and X.509 Certificates

When acquiring credentials for a server-side application, or for a client that is expected to present its credentials later in the SSL handshake, one very important member of the SCHANNEL_CRED structure should be looked at: paCred -- a pointer to an array of CERT_CONTEXT structures (cCreds -- the number of structures in the array). The CERT_CONTEXT structure contains an X.509 certificate [4] and the matching context in the form of a HCERTSTORE handle. Typically, an application will have one set of the CERT_CONTEXT structures. If it's not enough, multiple certificate contexts can be loaded and specified.

A CERT_CONTEXT structure is obtained in the number of ways. In the accompanying sample application I'm extracting the certificate from the system's certificate store using the CSecCertificate wrapper class and its function -- GetCertificate(). If you decide to go the same route, you'll need to open the store with CertOpenSystemStore() and look up the certificate by the principal's name with CertFindCertificateInStore(). By default Windows maintains four stores -- MY, CA (Certification Authority), ROOT, and SPC (Software Publisher Certificates). You can examine them by going to the Internet Explorer properties dialog and clicking on the Content tab and then the Certificates... button. Also by default, when you request a certificate from a Certification Authority (CA) that is based on Microsoft Certificate Server, Windows will add it to one of the system stores. Of course, there are ways to change that behavior. For example, you can create your own certificate store (look at certificate-related functions in [5]) and import the certificates. The store can be an internal file, or a registry blob, or what have you. If the software you are developing is for the in-house utilization only, then the best way to avoid higher costs associated with valid certificates issued by CA Root Authorities such as VeriSign is to install an in-house certificate server and establish your company's own Certification Authority. Certificate servers are beyond the scope of this article but you can easily install them on WinNT/2000/XP server platforms. For WinNT machines they can be downloaded and installed as part of the NT Option Pack, for 2000 and XP they are part of the system installation package. At the reader's disposal I provide a test certificate in the form of a PKCS#12 [6] file, which contains the certificate and the matching private key. To import the certificate and the key, simply double-click on the file and follow the wizard's directions. Be aware that the certificate has been issued and signed by Nebula Technologies CA, which is not part of the root certificate store on your machine. You'll have to suffer with an annoying dialog revealing that the certificate's path cannot be verified, although, this will suffice for testing purposes.

Finding the certificate in the system store will work if you already have a certificate and it was successfully imported into the store. Once the certificate is found your application may try to verify it before putting it to use. A more generic version, CertOpenStore(), can open a store that has been previously serialized into an external file. Or, it can open a PKCS#7 [7] file containing X.509 certificate chain. Another way is to manually create a certificate context from an existing X.509 certificate and add it to the previously opened store, thus obtaining both the certificate and the store context. The functions to look at are CertCreateCertificateContext() and CertAddCertificateContextToStore(). In all the above cases of acquiring the certificate, it's implied that the private key that corresponds to the certificate's public key exists in the security provider repository. Otherwise, your application will not be able to negotiate SSL read/write keys and calls to AcceptSecurityContext() or InitializeSecurityContext() will fail.

SSL Handshake and Data Exchange

Once AcquireCredentialsHandle() function successfully returns the handle, the next step in getting SChannel to do its work is to call AcceptSecurityContext() or InitializeSecurityContext() to perform the SSL handshake for us. The first function processes handshake messages as a server and the second one as a client. After that, EncryptMessage() and DecryptMessage() are used for just what their names imply -- encrypting and decrypting messages. Feel free to look up these functions [5] and the meaning of their input/output parameters and return codes (which are plenty, by the way). In the supplemental code, CSslTransport class wraps these important functions. I sprinkled a lot of comments there in an attempt to explain what's behind all these SEC_E_INCOMPLETE_MESSAGE and SECBUFFER_EXTRA constants that inform the caller about the actions taken by SChannel or actions that the caller has to take on its part when the functions return.

Conclusion

I'm barely touching the tip of the iceberg of what's involved in creating a robust and responsive SSL-enabled client or server system, especially the server. If you anticipate a heavy load on that system, you should consider some optimization techniques. Eric Rescorla's book [8] outlines most of the problems related to SSL and TLS performance. The ability to support the maximum number of clients is of paramount importance for the server that anticipates heavy traffic. If you are using WinSock as the data supplier for the application, consider loading WinSock 2 and utilizing its bandwidth throttling capability [11].

To get a sense of how SChannel handles the data, try playing with the supplemental code at the CUJ website. The supplemental code provides a good and verbose diagnostic feature. Good luck.

References

[1] Freier, A.O., Karlton, P., and Kocher, P.C., "The SSL Protocol Version 3.0." November 1996. This is the last published draft of SSLv3, which never materialized as an RFC but became the de facto standard. <http://home.netscape.com/eng/ssl3/draft302.txt>

[2] Dierks, T., Allen, C., "The TLS Protocol Version 1.0," RFC 2246. January 1999.

[3] ITU-T, "OSI networking and system aspects -- Abstract Syntax Notation One (ASN.1)," ITU-T Recommendation X.690, December 1997.

[4] ITU-T, "Directory," ITU-T Recommendation X.509, August 1997.

[5] Microsoft, "MSDN Help." April 2002.

[6] RSA Laboratories, "Password Based Encryption Standard," PKCS #12. June 1999.

[7] RSA Laboratories, "Cryptographic Message Syntax Version 1.5," PKCS #7. November 1993.

[8] Rescorla, E., SSL and TLS: Designing and Building Secure Systems. Addison-Wesley, 2001.

[9] Thomas, S., SSL and TLS Essentials. John Wiley & Sons, Inc., 1999.

[10] Linn, J., "Generic Security Service Application Program Interface (GSS-API)," RFC 1508. September 1993.

[11] Hua W., "Winsock 2: QoS API Fine-Tunes Networked App Throughput and Reliability." MSDN Magazine, Microsoft, April 2001.

About the Author

Alen Talibov holds MS in electrical engineering from Marine Technical University in St.Petersburg, Russia. He started his career as an acoustical engineer moving on to teaching Computer Science and Operating Systems disciplines to college graduates. Since 1993, information technology became his top priority. After coming to the US in 1995 he's been working as a software engineer for a retail company, financial institutions and startup and manufacturing companies. Right now Alen Talibov works as a software consultant in the metropolitan Boston area. He can be reached at [email protected].


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.