The first two parts of this series discuss creating client-side applications using OpenSSL. Part 1 discusses creating a basic secure client using OpenSSL, while Part 2 talks more in depth about digital certificates. After a few e-mails and some positive feedback from readers of these articles, it was clear that the next logical discussion should be about servers.
Servers provide a network and the Internet with access to resources such as files and devices. At times it is necessary to provide these services across a secure channel. OpenSSL lets you write services using both secure and open channels.
Creating a basic server application using OpenSSL is almost identical in nature to creating a basic client application. The differences are relatively few. One, obviously, is that the server will be set up to accept incoming connections instead of creating outgoing connections. And, as you might recall from the digital certificates discussion in Part 2 of this series, the server must also provide the security certificate used during the handshake.
Servers pretty much just sit and wait for incoming connections. After all, that's what they're there for. Web servers wait for browsers to request pages, FTP servers wait for clients to request files, and chat servers wait for incoming chat client connections. They just wait.
There is very little difference between secure client and server communication, except that the server is the reverse side of the coin in terms of the handshake. Everything else is the same.
This makes writing a secure server application with OpenSSL a piece of cake, assuming you know how to write a client application with OpenSSL. (If you haven't already done so, please read Part 1 in the series, "Overview of the API," to learn how to set up the OpenSSL library.)
Two forms of identification, please
Or, rather, two parts to the identification.
The server is responsible for providing the security certificate that will be used during the handshake. The complete server certificate consists of two parts: a public key and a private key. The public key is what is sent off to the client, while the private key is kept private.
Just as the trust certificates must be provided to the library for a client application, the server keys must be provided to the library for a server application. There are several functions that provide this:
Listing 1. Functions to load a server certificate
SSL_CTX_use_certificate(SSL_CTX *, X509 *) SSL_CTX_use_certificate_ASN1(SSL_CTX *ctx, int len, unsigned char *d); SSL_CTX_use_certificate_file(SSL_CTX *ctx, const char *file, int type); |
The ASN1 variety of this function loads an ASN1-encoded digital certificate from the specified memory location into the SSL context. The first function loads an X.509 certificate provided in the given memory structure, while the last function, the _file one, loads a PEM-encoded digital certificate from a file. The type parameter of that function allows a DER-encoded certificate to be loaded.
To load the private key, use one of the following functions:
Listing 2. Functions for loading a private key
SSL_CTX_use_PrivateKey(SSL_CTX *ctx, EVP_PKEY *pkey); SSL_CTX_use_PrivateKey_ASN1(int pk, SSL_CTX *ctx, unsigned char *d, long len); SSL_CTX_use_PrivateKey_file(SSL_CTX *ctx, const char *file, int type); SSL_CTX_use_RSAPrivateKey(SSL_CTX *ctx, RSA *rsa); SSL_CTX_use_RSAPrivateKey_ASN1(SSL_CTX *ctx, unsigned char *d, long len); SSL_CTX_use_RSAPrivateKey_file(SSL_CTX *ctx, const char *file, int type); |
Any private key is best stored encrypted. The trouble, though, is that the functions that load the certificates do not ask for a password for encrypted certificates. Instead, OpenSSL provides a callback mechanism for obtaining the password.
The format of the callback is:
Listing 3. Callback format
int password_callback(char *buf, int size, int rwflag, void *userdata); |
For the purposes of this article, the final parameter, userdata, is not necessary. The buffer is allocated before this function is called, so you have no control over how large the buffer is.
The parameter rwflag is the read/write flag. It is used so you can programmatically determine if the password is being used to encrypt information (rwflag = 1) or decrypt information (rwflag = 0). If the callback is being used to request a password for encrypting data, it is preferable to ask for the password twice, in some fashion, to catch any typos.
The password is only requested once when the certificate is loaded so that it can be decrypted and stored in memory. How the password is obtained from the user is entirely up to your implementation.
Once you have your password callback function created, you install it into the SSL context using SSL_CTX_set_default_passwd_cb as follows:
Listing 4. Installing the callback function
/* ctx is a pointer to a previously created SSL context, and cb is the pointer * to the callback function you created. */ SSL_CTX_set_default_passwd_cb(ctx, cb); |
Putting the key in the ignition
Now that the callback function has been created so the user can be prompted for a password, the functions to actually import the certificates can be used. The certificate can be imported from an existing memory structure or a file.
To be more in line with common practice in handling digital certificates, such as that of the Apache HTTP Server Project, I'll demonstrate how to load a certificate from a file. If you've read Part 1 in this series, loading a certificate is similar to the way the trust store is loaded in the demonstration given in that earlier article.
I'll start with the public certificate, which is the one that gets sent to the client.
Listing 5. Loading the public certificate
/**
* ctx is the SSL context created earlier
*/
if(SSL_CTX_use_certificate_file(ctx, "/path/to/certificate.pem", SSL_FILETYPE_PEM) < 1)
{
/* Handle failed load here */
}
|
After the public certificate is loaded, the private certificate must be loaded. This part is needed during the handshake, because the client will be sending information to the server encrypted to the public certificate. That data can only be decrypted using the private key. Again, to keep things consistent, I'm loading the key from a file.
Listing 6. Loading the private key
if(SSL_CTX_use_PrivateKey_file(ctx, "/path/to/private.key", SSL_FILETYPE_PEM) < 1)
{
/* Handle failed load here */
}
|
After setting up the context (see the SSL context sidebar, above) and loading the keys, now is the time to finish the setup by creating the BIO object. You may recall from Part 1 how I established both SSL and non-SSL communication using OpenSSL's BIO library. To be consistent with that article, the same will be done here.
Listing 7. BIO pointers
BIO *bio, *abio, *out; |
Three BIO objects? Why do we need three? They all have a purpose, trust me. (Remember, trust and security go together.)
The first one, bio, is the main BIO object that will be created from the SSL context. The second object, abio, is the accept BIO, the one that will accept incoming connections. And the third BIO, out, is what the server will be talking through to the client.
Listing 8. Setting up the main BIO object
bio = BIO_new_ssl(ctx, 0);
if(bio == NULL)
{
/* Handle failure here */
}
/* Here, ssl is an SSL* (see Part 1) */
BIO_get_ssl(bio, &ssl);
SSL_set_mode(ssl, SSL_MODE_AUTO_RETRY);
|
Setting up the BIO object here is a little different from setting it up for a client connection. You may recall from Part 1 that a client connection is set up using BIO_new_ssl_connect.
Here, the setup is done using BIO_new_ssl with two parameters: a pointer to an SSL_CTX object and a flag. This flag tells OpenSSL what kind of BIO object to create: 0 for server, 1 for client. Since this code is attempting to set up a client connection, the flag is set to 0.
Listing 9. Setting up the accept BIO
abio = BIO_new_accept("4422");
BIO_set_accept_bios(abio, bio);
|
Where BIO_do_connect creates a BIO for client connections, BIO_new_accept creates a BIO for server connections. It takes just one argument, the port to listen on encoded in a string.
Because this is supposed to be listening for secure connections, we need to chain a secure BIO onto this accept BIO. That is where the second function call, BIO_set_accept_bios, comes into play. It chains the previously created SSL BIO onto the accept BIO.
This function call also takes away the need to free the SSL BIO. It will be automatically freed when the accept BIO is destroyed.
A server is fisherman of sorts; it just sits and waits until a client bites. Servers play the waiting game, just waiting for an incoming connection.
If you've had any experience with Winsock or BSD Sockets, you've probably come across the function accept. The OpenSSL counterpart is BIO_do_accept, except that where you could call accept once to have it sit and wait, you must call BIO_do_accept twice before it will sit and wait.
Listing 10. Telling the server to sit
/* First call to set up for accepting incoming connections... */
if(BIO_do_accept(abio) <= 0)
{
/* Handle fail here */
}
/* Second call to actually wait */
if(BIO_do_accept(abio) <= 0)
{
/* Handle fail here */
}
/* Any other call will cause it to wait automatically */
|
The first call to BIO_do_accept sets the BIO up to accept incoming connections. The second call is needed to actually get it to sit and wait. Any subsequent time after that will allow it to just wait.
BIO_do_accept will return 1 when it receives an incoming connection. But you can't just talk through the accept BIO. Instead, OpenSSL creates another BIO that must be popped off of the accept BIO using BIO_pop.
Listing 11. Popping the connection to talk
out = BIO_pop(abio);
if(BIO_do_handshake(out) <= 0)
{
/* Handle fail here */
}
|
After popping the incoming connection off the accept BIO, the handshake needs to be handled with a call to BIO_do_handshake. If the setup from the previous sections succeeded, then handshake should succeed as well.
The server would actually talk with the client through the various read and write functions available to the BIO library. I talked about those in Part 1, so you can find more discussion there.
Overall, creating a secure server application with OpenSSL is not difficult once you understand the basics of how it's done. From here, you can extend the code samples provided to create a full SSL server application to suit your needs. Be forewarned, however, that the code samples provided here and in the Downloads section below are minimal at best and should be used only for experimentation purposes. Before actually creating a full SSL server application, be sure to read and research the latest security recommendations.
| Description | Name | Size | Download method |
|---|---|---|---|
| Code samples for this article | openssl3.tar.gz | 4KB | HTTP |
Information about download methods
Learn
-
Read Part 1 in this series, "Overview of the API: Creating basic secure and unsecure connections" (developerWorks, July 2004) for a primer on how to set up the OpenSSL library and create a simple client.
-
Read Part 2 in this series, "Secure handshake: Avoid a man in the middle (MITM) attack" (developerWorks, May 2005) to learn some basics on handling digital certificates, including how to retrieve and verify the name from the certificate.
-
In the developerWorks Linux zone, find more resources for Linux® developers.
-
Stay current with developerWorks technical events and Webcasts.
Get products and technologies
-
Download the latest OpenSSL library and related documentation.
-
Order the SEK for Linux, a two-DVD set containing the latest IBM trial software for Linux from DB2®, Lotus®, Rational®, Tivoli®, and WebSphere®.
-
With IBM trial software, available for download directly from developerWorks, build your next development project on Linux.
Discuss
-
Check out developerWorks
blogs and get involved in the developerWorks community.
Kenneth is a Software Engineer working for the MediNotes Corp. in West Des Moines, Iowa. He graduated from Peru State College in Peru, Nebraska, with a Bachelor of Science in Business Administration. He also has an Associate of Science in Computer Programming from Southwestern Community College in Creston, Iowa. Kenneth has written several applications and programming libraries.