Chapter 4 Using Secure Sockets
4.1 Getting Started with Secure Sockets
The following examples demonstrate some simple client and server programs using both the Communication Adapter Layer classes (through RWSecureSocket) and the Portal Layer classes (through RWSecureSocketPortal).
The examples in this section depend on the macros TRUSTED_CERTS_FILE, SERVER_DATA_FILE, SERVER_CERT_FILE, and SERVER_PRIVATE_KEY_FILE, which are defined in examples\secsock\secsockexampledefs.h. Several of the examples use these macros to determine certificates, servers, and ports.
4.1.1 Writing a Client Program That Uses RWSecureSocket
Example 1 is a client program that creates a secure socket and sends a message.
This example uses utility functions provided in util.h.
Example 1 – Client program that uses RWSecureSocket
// File: examples\secsock\manual\RWSecureSocketSimpleClient.cpp
 
#include <rw/network/RWWinSockInfo.h>
#include <rw/network/RWInetAddr.h>
#include <rw/secsock/RWSecureSocketPackageInit.h>
#include <rw/secsock/RWSecureSocketContext.h>
#include <rw/secsock/RWSecureSocket.h>
 
#include <iostream>
using std::cout;
using std::cerr;
using std::endl;
 
#include <secsockexampledefs.h>
include <util.h>
 
int main(int argc, char **argv)
{
try {
RWWinSockInfo info; //1
RWSecureSocketPackageInit secsockInit; //2
#if defined(RW_SECSOCK_RNG_NEEDS_SEEDING)
RWSecureSocketPackageInit::seedRNGFromFile
(SEED_DATA_FILE); //3
#endif
 
int port = parseClientCommandLine(argc, argv);
 
RWSecureSocketContext context; //4
context.prepareToAuthenticate(TRUSTED_CERTS_FILE); //5
 
RWInetAddr addr(port, “localhost”) //6
RWSecureSocket sock(context); //7
 
sock.connect(addr); //8
sock.sendAtLeast("Hello World!"); //9
sock.close(); //10
} catch(const RWInternalErr& ie) {
cerr << ie.why() << endl;
return 1;
} catch(const RWExternalErr& ee) {
cerr << ee.why() << endl;
return 1;
}
 
return 0;
} //11
//1 Constructs an RWWinSockInfo instance. Initializes the Winsock library under Windows, but has no effect under Unix.
//2 Initializes the Secure Sockets package and the underlying cryptographic library.
//3 Seeds the random number generator, if it is necessary on your platform. Uses the data stored in SEED_DATA_FILE. This file can be any file on your system. See Section 4.4.1, “Seeding the Random Number Generator,” for more information.
//4 Constructs a context object that holds the default parameters for all secure sockets and portals that you create.
//5 Gets the path and file name of the trusted certificates file. In this example, TRUSTED_CERTS_FILE, is located in the same directory as the executable.
The client side of an SSL/TLS connection needs a set of trusted certificates to use when verifying a server. This set of trusted certificates should include the certificate of the server and the certificate of every certificate authority that signed your server’s certificate, including the root certificate authority. Every certificate that your client trusts should be placed in a single file. For more information, see Section 4.3, “Obtaining Certificates.”
//6 Creates an address object that refers to the specified port on “localhost” (the local machine).
//7 Creates a secure socket from the context
//8 Connects the socket to the specified port on “localhost”.
//9 Sends a message over the socket.
//10 Closes the socket.
//11 Constructs destructors for RWWinSockInfo and RWSecureSocketPackageInit to clean up resources allocated by the underlying Winsock and cryptographic libraries.
4.1.2 Writing a Client Program That Uses RWSecureSocketPortal
Example 2 is a client program that uses a portal to create a secure socket and send a message. This client is identical to the client in Example 1, except that it uses the Portal layer classes.
This example uses utility functions provided in util.h.
Example 2 – Client program that uses RWSecureSocketPortal
// File: examples\secsock\manual\
// RWSecureSocketPortalSimpleClient.cpp
 
#include <rw/network/RWWinSockInfo.h>
#include <rw/network/RWInetAddr.h>
#include <rw/secsock/RWSecureSocketPackageInit.h>
#include <rw/secsock/RWSecureSocketContext.h>
#include <rw/secsock/RWSecureSocketPortal.h>
 
#include <iostream>
using std::cout;
using std::cerr;
using std::endl;
 
#include <secsockexampledefs.h>
#include <util.h>
 
int main(int argc, char **argv)
{
try {
RWWinSockInfo info;
RWSecureSocketPackageInit secsockInit;
#if defined(RW_SECSOCK_RNG_NEEDS_SEEDING)
RWSecureSocketPackageInit::seedRNGFromFile
(SEED_DATA_FILE);
#endif
 
int port = parseClientCommandLine(argc, argv);
 
RWSecureSocketContext context;
context.prepareToAuthenticate(TRUSTED_CERTS_FILE);
RWInetAddr addr(port, "localhost");
RWSecureSocketPortal portal(addr, context); //1
 
portal.sendAtLeast("Hello World!"); //2
 
// portal goes out of scope here //3
} catch(const RWInternalErr& ie) {
cerr << ie.why() << endl;
return 1;
} catch(const RWExternalErr& ee) {
cerr << ee.why() << endl;
return 1;
}
 
return 0;
}
//1 Creates a portal to the specified address using the specified context. The portal automatically creates a socket and connects it to the address.
//2 Sends a message.
//3 Closes the underlying socket automatically, as long as no other part of the program is holding a copy of the portal. The destructors for RWWinSockInfo and RWSecureSocketPackageInit are also executed here to clean up the Windows socket library and the underlying cryptographic library.
4.1.3 Writing a Server Program That Uses RWSecureSocket
Example 3 is a server program that creates a secure socket that waits for requests.
This example uses utility functions provided in util.h.
Example 3 – Server program that uses RWSecureSocket
// File: examples\secsock\manual\RWSecureSocketSimpleServer.cpp
 
#include <rw/network/RWWinSockInfo.h>
#include <rw/network/RWInetAddr.h>
#include <rw/secsock/RWSecureSocketPackageInit.h>
#include <rw/secsock/RWSecureSocketContext.h>
#include <rw/secsock/RWSecureSocket.h>
 
#include <iostream>
#include <fstream>
using std::ifstream;
using std::cout;
using std::cerr;
using std::endl;
 
#include <secsockexampledefs.h>
#include <util.h>
 
int main(int argc, char **argv)
{
try {
RWWinSockInfo info; //1
RWSecureSocketPackageInit secsockInit; //2
#if defined(RW_SECSOCK_RNG_NEEDS_SEEDING)
WSecureSocketPackageInit::seedRNGFromFile
(SEED_DATA_FILE); //3
#endif
int port = parseCommandLine(argc, argv);
 
RWSecureSocketContext context; //4
 
ifstream certf(SERVER_CERT_FILE);
RWX509Certificate cert(certf); //5
ifstream keyf(SERVER_PRIVATE_KEY_FILE);
RWPrivateKey key(keyf); //6
context.setIdentity(cert, key); //7
 
RWInetAddr addr(port, "localhost"); //8
RWSecureSocket listenSock(context); //9
listenSock.bind(addr); //10
listenSock.listen(); //11
 
// Display the port actually bound to
cout << listenSock.getsockname() << endl;
 
char buf[64];
for(volatile bool spin = true; spin ; /* */) {
RWSecureSocket newSock = listenSock.accept(); //12
buf[newSock.recv(buf, 63)] = 0; //13
cout << "Received: " << buf << endl; //14
newSock.close(); //15
} //for
} catch(const RWInternalErr& ie) {
cerr << ie.why() << endl;
return 1;
} catch(const RWExternalErr& ee) {
cerr << ee.why() << endl;
return 1;
}
 
return 0; //16
}
//1 Constructs an RWWinSockInfo instance. Initializes the Winsock library under Windows, but has no effect under Unix.
//2 Initializes the Secure Sockets package and the underlying cryptographic library.
//3 Seeds the random number generator, if necessary on your platform. Uses the data stored in SEED_DATA_FILE. See Section 4.4.1, “Seeding the Random Number Generator,” for more information.
//4 Constructs a context object that holds the default parameters for all sockets and portals that you create.
//5 Constructs the server’s certificate by using an istream to read the certificate from SERVER_CERT_FILE.
//6 Constructs the server’s private key by using an istream to read the key from SERVER_PRIVATE_KEY_FILE.
//7 Associates the server’s key and certificate with the context and specifies that this program will be acting as a server with the given identity.
//8 Creates an address object that refers to the specified port on "localhost" (the local machine).
//9 Creates a secure socket from the context
//10 Binds the listener socket to the address and port.
//11 Listens for connections and sets up a queue for them. Initializes the data structures.
//12 Blocks until a connection request arrives.
//13 Reads no more than 63 characters.
//14 Displays the string received in line //13.
//15 Closes the socket.
//16 Executes destructors for RWWinSockInfo and RWSecureSocketPackageInit to clean up their associated libraries.
4.1.4 Writing a Server Program That Uses RWSecureSocketPortal
Example 4 is a server program that uses a portal to create a secure socket that waits for requests. Comments are included only for code that is different than Example 3.
This example uses utility functions provided in util.h.
Example 4 – Server program that uses RWSecureSocketPortal
// File:
// examples\secsock\manual\RWSecureSocketPortalSimpleServer.cpp
 
#include <rw/network/RWWinSockInfo.h>
#include <rw/network/RWInetAddr.h>
#include <rw/secsock/RWSecureSocketPackageInit.h>
#include <rw/secsock/RWSecureSocketContext.h>
#include <rw/secsock/RWSecureSocketPortal.h>
#include <rw/secsock/RWSecureSocketListener.h>
#include <rw/rstream.h>
 
#include <iostream>
#include <fstream>
using std::ifstream;
using std::cout;
using std::cerr;
using std::endl;
 
#include <secsockexampledefs.h>
#include <util.h>
 
int main(int argc, char **argv)
{
try {
RWWinSockInfo info; //1
RWSecureSocketPackageInit secsockInit; //2
#if defined(RW_SECSOCK_RNG_NEEDS_SEEDING)
RWSecureSocketPackageInit::seedRNGFromFile(SEED_DATA_FILE);
#endif
int port = parseServerCommandLine(argc, argv);
 
RWSecureSocketContext context; //3
 
ifstream certf(SERVER_CERT_FILE);
RWX509Certificate cert(certf); //4
ifstream keyf(SERVER_PRIVATE_KEY_FILE);
RWPrivateKey key(keyf); //5
context.setIdentity(cert, key); //6
 
RWInetAddr addr(port, "localhost"); //7
RWSecureSocketListener listener(addr, context); //8
 
// Display the port actually bound to
cout << listener.getSocket().getsockname() << endl;
 
char buf[64];
for(volatile bool spin = true; spin ; /* */) {
RWSecureSocketPortal newPortal = listener(); //9
 
buf[newPortal.recv(buf, 63)] = 0; //10
cout << "Received: " << buf << endl; //11
}
} catch(const RWInternalErr& ie) {
cout << ie.why() << endl;
return 1;
} catch(const RWExternalErr& ee) {
cout << ee.why() << endl;
return 1;
}
 
return 0;
}
 
//1 Initialize Windows Sockets library (does nothing on UNIX).
//2 Initialize the Secure Sockets package.
//3 Create a context instance to hold information related to the SSL connection.
//4 Load the server's certificate.
//5 Load the server's private key.
//6 Associate the certificate and private key with the context.
//7 Create an address instance which will be where the server listens for connections.
//8 Create the listener instance passing the address to bind to and the context which contains the SSL connection information.
//9 The function call operator (operator()) on a listener blocks and waits for a connection to arrive. Upon connection a portal to the newly created socket is returned.
//10 Use the new portal. In this case we call recv() waiting for the client to send some data.
//11 The data sent by the client is printed to standard output.
4.1.5 About the Keys and Certificates in the Example Programs
The keys and certificates supplied with the example programs were created using OpenSSL. They are located in the examples\certs directory. In subdirectories of that directory, readme.txt files describe the certificate infrastructure used for the examples and the scripts used to create them.
The scripts are provided on an as-is basis. Technical support will not be provided for them. Rogue Wave does support the example programs.