Wiki

Secure Networking with QtSSLSocket

by Andreas Aardal Hanssen
Secure networking has two main requirements. Firstly, you must be sure that you are communicating with your intended recipient. Secondly, you need to be confident that the data exchanged has not been tampered with or even read by a third party; and that even if it was read, it could not be deciphered. This article shows how to create a secure client using the OpenSSL library and the QtSSLSocket Qt Solution that meets these requirements.

The SSL (Secure Sockets Layer) is the standard protocol providing network level security. SSL is a software layer on top of the TCP layer, that uses TCP packets to send encrypted data. [1]

An SSL transmission has three phases: the negotiation (or handshake) phase, the data exchange phase, and the shutdown phase. During the handshake phase, the server's (and optionally the client's) identity is verified through the exchange of public keys. The peers then agree on a common algorithm to use for encrypting data in the data exchange phase. The cryptographic algorithms are seeded with random data, and the data exchange phase begins.

At this point you know who your client has connected to (or who connected to your server). The data exchange phase is where the real user data is sent between the client and the server. This data is encrypted with algorithms such as "DES3" or "Blowfish". These algorithms are considered to be safe from tapping, tampering, and replaying. When the data exchange phase ends, the shutdown phase starts. Here, the client and the server ensure that each has terminated the secure channel and that it is no longer carrying data. The socket connection is then closed.

The number of professional networking services on the Internet that require SSL is both vast and growing. For example, you may want to submit purchase orders or to perform credit card transactions, and for these, banks will insist upon the use of SSL. But Qt's networking support, though good, does not include provision for SSL "out-of-the-box"; indeed there is no straightforward way to enable SSL in Qt applications.

One obvious solution is to link your Qt application against an SSL library. But many of these libraries are complex, with difficult APIs, and either little or poor documentation. And all of them require in-depth knowledge of the SSL procotol and its implementation. So what can you do if you don't know what an X.509 certificate looks like, but need secure networking?

The answer is to use the QtSSLSocket from the Qt Solutions team. QtSSLSocket allows QSocket-based networking applications to add SSL cryptographic support. This class has an easy-to-use interface, and requires no prior knowledge about the SSL protocol. This class provides a socket for your data blocks, conveniently encrypting what you send and decrypting what you receive from your peer. Using QtSSLSocket, you can easily add SSL to both your client and server applications, while leaving elbow room for providing SSL encryption for protocols of any level of complexity. And the little SSL knowledge you need to know is in the documentation.

How secure is SSL?
Without a secure networking model, you cannot be certain of who you are really connecting to on the Internet. DNS entries may be forged, the Internet host may be replaced, the data transmission may be compromised or tapped, and so on. On the other hand, the SSL security model provides a very reliable guarantee of the identity of the host you are connecting to, by allowing you to verify the host's identity through a trusted third party, known as a Certificate Authority (CA). Using the CA's certificate, you can verify the authenticity of the peer's certificate, and therefore the peer's identity. The mechanism does of course depend on the CA being trusted.

But how can you verify the identity of the CA's certificate? SSL requires that each application has access to a static bundle of certificates for all the trusted CAs. If you don't have the certificate of the CA that issued your peer's certificate, you cannot verify the host's identity. But how can the end user safely obtain such a bundle of CA certificates? Obviously they cannot be downloaded from the Internet using an SSL connection. Therefore, most modern operating systems include a vendor-supplied package that contains a bundle of CA certificates. But if your operating system was installed from a CD you bought at a store or got delivered by mail, you must trust all the postal services involved in the delivery, and the employees at the store, and of course the manufacturer of the CD. And if you downloaded your operating system from the Internet, are you sure that your connection was secure?

Let's make a simple SSL client. We can write a complete example in just 50 lines. Let's start with the definition of our Client class.

class Client : public QObject
{
    Q_OBJECT
 
public:
    Client(const QString &host, int port);
 
signals:
    void responseReceived();
 
private slots:
    void waitForGreeting();
    void readResponse();
 
private:
    QtSSLSocket *socket;
};

Just like any other QSocket client, we create a subclass with a socket (the QtSSLSocket in this case) as a private member.

Client::Client(const QString &host, int port)
{
    socket = new QtSSLSocket();
    connect(socket, SIGNAL(connected()), SLOT(waitForGreeting()));
    connect(socket, SIGNAL(readyRead()), SLOT(readResponse()));
    connect(socket, SIGNAL(connectionClosed()), qApp, SLOT(quit()));
    connect(socket, SIGNAL(delayedCloseFinished()), qApp, SLOT(quit()));
    socket->connectToHost(host, port);
}

In the constructor, we create our SSL socket and connect its signals, then connect to the host.

void Client::waitForGreeting()
{
    qDebug("Connected; now waiting for the greeting");
}

The waitForGreeting() slot is called when the connection has been established.

void Client::readResponse()
{
    if (socket->canReadLine()) {
        qDebug("Received: %s", socket->readLine().latin1());
        socket->close();
    }
}

The readResponse() slot is just as easy as it would be with a normal QSocket subclass.

When the connection has been closed, the application will terminate. And that's it! For the sake of completeness, here's the main function. We used this example to connect to our IMAP server, which runs on port 993:

int main(int argc, char *argv[])
{
    QApplication app(argc, argv, false);
    Client client("imap", 993);
    return app.exec();
}

Running this application gives the following output to the console, depending on what IMAP software you are running:

Connected! Now waiting for the greeting
Received: * OK imap Cyrus IMAP4 v2.1.12 server ready

encryption
phases

So, is this all that's needed to communicate safely with SSL? Well, not quite. We can't tell whether or not the host's identity check succeeded during the negotiation phase; there is no code in our example handling this important security feature. Let's add the necessary code to rectify this deficiency.

First, we set the location of the CA certificate bundle. For the sake of this example, we use the path /etc/ssl/certs, but the location of the bundle is different for different operating systems and distributions. We insert this line into the constructor:

 socket->setPathToCACertDir("/etc/ssl/certs");

Then, we add a slot to catch certificate check failures:

private slots:
    void displayCertError(int, const QString &reason)

Next we connect our socket's signal to this slot:

  connect(socket, SIGNAL(certCheckFailed(int, const QString &)),
                  SLOT(displayCertError(int, const QString &)));

Now we implement the displayCertError() slot.

void Client::displayCertError(int, const QString &reason)
{
    qDebug("Certificate check failed: %s", reason.latin1());
    socket->close();
}

The slot's implementation outputs an error message and closes the connection. This means that if the certificate cannot be verified, the communication is terminated rather than risking an insecure connection.

The output from the modified program:

Certificate check failed: Self signed certificate in certificate chain

This is all that you need to do to add secure networking to your Qt applications.

If you want to learn more, the QtSSLSocket documentation has a guide to handling SSL private keys, certificates and Certificate Authorities.

Note that QtSSLSocket depends on OpenSSL. The OpenSSL library is licenced under an Apache-style license, which essentially means that you are free to use it for both commercial and non-commercial purposes subject to some simple license conditions; see www.OpenSSL.org.

SSL Tunneling
A common alternative to using SSL from an application is to use an SSL tunnel such as Stunnel (available from www.Stunnel.org). Stunnel runs as a separate service which your application can connect to. Stunnel connects to the SSL host on your behalf, encrypting your outgoing data before it is sent, and decrypting incoming data as it arrives.

Unfortunately, an SSL tunnel is not a general solution. It doesn't solve client side networking in general, as each tunnel server in client mode can only forward data to one server while your application often needs to send to several servers. Also, although it works fine with single connection client-server protocols such as HTTP, it can't help you add SSL to FTP, where the port on which you listen for incoming connections is only known at run-time. Most importantly though, an SSL tunnel requires you to run a separate server alongside your application. This means that you must distribute extra software, and for commercial applications, this may involve additional licensing costs.


[1] Because of the unreliable nature of UDP datagrams, the SSL protocol doesn't support UDP.


Copyright © 2004 Trolltech Trademarks