Using Self-Signed Certificates in Qt Code
One of the standard architectures of software systems is to have an application that talks to a server in some form. For example, a Qt application that talks to an Apache-based server with a REST interface. We see this with many of the applications we write for customers.
The code in this blog will be Qt and C++ based. However, once the SSL certificate has been added to the system, it's perfectly fine to use the certificate from QML requests as well.
There are several steps that need to happen to make this connection secure. One is to use an encrypted connection for which this article will focus on. This is not the only thing that needs to happen to secure the system, but it is one of the first steps.
This article will show how to create a self-signed SSL key for Apache and how the Qt code will use it. If you have an official set of SSL keys for your web server, then you can use those to secure the REST calls as well. In case you don't, or if the REST API is running on a URL not supported by your keys, then a self-signed key could be a solution.
The use of self-signed certificates is usually frowned upon. In this case, it is only used between the application and the REST server. This way there is no security problem involved and the user cannot see the implementation. There is no value in paying for a certificate that isn't visible to the user.
The first step is to generate the SSL key and certificates. This is done with OpenSSL in three steps.
In this example, I will create a key for a hypothetical company called "XYZ, Inc." located in Boston. They have the domain xyz.com, but the REST server is running on api.xyz.com.
Step one is to generate the key. I chose not to set a passphrase on the key because it's used by Apache.
openssl genrsa -out api.xyz.com.key 2048
Step two is to generate a certificate signing request for the key. OpenSSL needs information or attributes for the key. Those are given in the subj argument. If you don't supply this on the command line, OpenSSL will ask.
openssl req -new -key api.xyz.com.key -out api.xyz.com.csr \
-subj "/C=US/ST=Massachusetts/L=Boston/O=XYZ Inc/CN=api.xyz.com/emailAddress=info@xyz.com"
Now you have a key and a certificate request and it's time to sign the key. A signing company like Verisign or Geotrust will charge for this step. I sign the key for a period of almost six years, but you can choose a timeframe suitable for your needs. I suggest you choose a reasonable value, otherwise OpenSSL will default to one month and that's usually not enough for a REST server.
openssl x509 -req -days 2000 -in api.xyz.com.csr -signkey api.xyz.com.key -out api.xyz.com.crt
At this point, you have a key and a certificate. The certificate file is what can be distributed to other people. The specific key is private and must be kept secret. Now, in this next step, you are ready to set up the Apache server to be able to serve HTTPS requests. It assumes you have copied the file api.xyz.com.key to /etc/apache2/ssl.key and api.xyz.com.crt (or whatever directory Apache stores keys in on your system).
<VirtualHost *:443>
SSLEngine on
SSLCertificateFile /etc/apache2/ssl.crt/api.xyz.com.crt
SSLCertificateKeyFile /etc/apache2/ssl.key/api.xyz.com.key
</VirtualHost>
On the Qt side, we have to import the certificate to be able to reach the server. I chose to do this by importing the certificate with the Qt resource system. The first step is to add a qrc file to your build. The file ssl.qrc looks like this:
<RCC>
<qresource prefix="/ssl">
<file>api.xyz.com.crt</file>
</qresource>
</RCC>
The final step is to add this certificate to the list of acceptable SSL certificates in the application. In the following chunk of code, the SSL certificate file is read and added to the list of certificates that can be used in all SSL based connections.
// Read the SSL certificate
QFile file(":/ssl/api.xyz.com.crt");
file.open(QIODevice::ReadOnly);
const QByteArray bytes = file.readAll();
// Create a certificate object
const QSslCertificate certificate(bytes);
// Add this certificate to all SSL connections
QSslSocket::addDefaultCaCertificate(certificate);
From this step, you can access the REST server with all your requests. There is nothing special in this code, it looks like any other HTTPS request. For example:
QNetworkRequest request("https://api.xyz.com/login");
QNetworkAccessManager().post(request, loginData);
And that's all there is to it. From this point on, all requests to api.xyz.com will accept and use your new SSL key.