The Cute Server Showcase: Encrypted Connections with Self-Signed Certificates

Photo by Kaffeebart on Unsplash

The Cute Server Showcase: Encrypted Connections with Self-Signed Certificates

In this post, we create and use self-signed certificates to encrypt connections on the Cute server. We will configure both one-way and two-way SSL authentication.

The Cute server is a next-gen server that allows developers to use the Qt's signals and slots mechanism to communicate over a network seamlessly. The Cute server abstracts everything related to the network from users, like sockets, servers, and safe, high-performance network data processing, enabling developers to focus on writing app-specific code instead of dealing with the hassles inherent to network programming.

The Cute server is innovative because it is like an OaaS (Object-as-a-Service) running on-premises. In the OaaS model that the Cute server provides, developers register classes with specially tagged signals and slots to endpoints on the server. Whenever a client connects to a mapped endpoint, the Cute server creates an instance of the mapped class and makes the instance's remote signals and slots available to the client. Clients interact with remote signals and slots as if they were local.

The Cute server provides a modern solution to network-based interaction. The OaaS model the Cute server provides is built upon the Qt's signals and slots mechanism and supports multiple programming styles. Developers can implement the legacy request/response model through direct remote slot calls. In addition, developers can create rich, stateful, two-way interactions through remote signal-slot connections and remote slot calls.

We introduced the Cute server in another post.

Encryption serves two purposes: validating peers and ensuring that only the validated peer can read our messages.

In essence, encryption is the process of scrambling/unscrambling data. Despite the misconception that encryption means integrity, in reality, encryption only guarantees confidentiality. Instead of exchanging plain data, with encryption, we exchange scrambled data that anyone in the connection chain can see and change. The Cute server uses HMAC to enforce message integrity on all its editions on both unencrypted and encrypted connections.

An encrypted connection begins with the TLS handshake, in which peers exchange SSL certificates to prove their identity and interact to generate a shared secret. Although the TLS handshake uses asymmetric encryption to validate peers and generate the shared secret, after the handshake, peers use symmetric encryption to encrypt data, which is much faster.

The validation process uses SSL certificates and their private and public keys. In one-way encryption, the server sends the client a certificate for validation. The client validates the certificate by checking if the certificate was signed by someone that the client trusts.

To use self-signed certificates in one-way SSL, we have to:

  1. Create a root private key and certificate.
  2. Create the server's private key and certificate.
  3. Sign the server's certificate using the root certificate and private key.
  4. Add the server certificate and private key to the server.
  5. Add the root certificate to the client.

We use OpenSSL to create the required certificates as follows:

# Create the root private key
openssl genrsa -out server-root-ca.key 4096
# Create the root certificate
openssl req -x509 -new -key server-root-ca.key -sha256 -days 3600 -out server-root-ca.crt -subj "/C=CC/ST=City/L=City/O=Org/OU=Org/CN=cute.server.tests.root-certificate"
# Create the server private key
openssl genrsa -out server.com.key 2048
# Create the server certificate sign request
openssl req -new -key server.com.key -out server.com.csr -subj "/C=CC/ST=City/L=City/O=Org/OU=Org/CN=cute.server.tests.certificate"
# Sign the server certificate using the root certificate and private key
openssl x509 -req -extfile <(printf "subjectAltName=DNS:example.com,IP:127.0.125.10") -in server.com.csr -CA server-root-ca.crt -CAkey server-root-ca.key -CAcreateserial -out server.com.crt -days 3000 -sha256

The commands above generate the server.com.crt certificate, the server.com.key private key, and the server-root-ca.crt certificate. We add the server certificate and private key to the server and the root certificate to clients. Generally, no certificate must be added to clients when using a certificate from a trusted source. The server can use its certificate to encrypt connections targeting both example.com and 127.0.125.10. Using IPs on SSL certificates is very useful during tests.

The Cute server fetches SSL certificates and private keys from the configuration file. We specify SSL certificates and private keys in the configuration file as follows:

[Common]
    licenseFilePath=/location/of/license.txt
    logTo = syslog
    logLevel = Info
    remoteObjectsLib = /tmp/fortunes-build/libRemoteObjectsLib.so
    workerCount = 1

[Listener.AllIPV4]
    address = 0.0.0.0
    port = 8440
    certificate = /location/of/server.com.crt
    privateKey = /location/of/server.com.key

Users can learn how to configure the Cute server on the Learning Hub. The licenseFilePath key in the configuration file holds the file path of a text file containing a valid subscription ID. The Cute server provides a free 30-day evaluation period on AWS EC2/Lightsail. In the AWS EC2/Lightsail environment and under the evaluation period, users do not have to provide the licenseFilePath key in the configuration file.

Now we have to add the root certificate to the client. Clients interact with remote objects on the Cute server through instances of the RemoteObject class, which accepts a QSslConfiguration instance in its constructor. We set up the QSslConfiguration and use it to create an instance of a RemoteObject as follows:

QVariant sessionData;
auto sslConfiguration = QSslConfiguration::defaultConfiguration();
QFile trustedCertFile("/location/of/server-root-ca.crt");
trustedCertFile.open(QIODevice::ReadOnly | QIODevice::ExistingOnly);
// Adding the root certificate to the client
auto serverRootCaCert = QSslCertificate::fromData(trustedCertFile.readAll());
sslConfiguration.setCaCertificates(serverRootCaCert);
RemoteObject remoteObject("ClassName",
    QUrl("cutes://example.com:8440/endpoint"),
    sessionData,
    sslConfiguration);

On blog.cuteserver.io, a post uses a Fortune example to illustrate how remote object interaction works with the Cute server.

Now we will set up and configure two-way SSL authentication on encrypted connections. Two-way SSL is like applying the one-way SSL process in both directions. In two-way SSL, servers validate clients, and clients validate servers. In addition to the certificates created above, we have to create the certificates used by servers to validate clients.

To create the certificate that clients send to the server, we have to:

  1. Create a root private key and certificate.
  2. Create the client's private key and certificate.
  3. Sign the client's certificate using the root certificate and private key.
  4. Add the client certificate and private key to the client.
  5. Add the root certificate to the server.

We use OpenSSL to create the required certificates as follows:

# Create the root private key
openssl genrsa -out client-root-ca.key 4096
# Create the root certificate
openssl req -x509 -new -key client-root-ca.key -sha256 -days 3600 -out client-root-ca.crt -subj "/C=CC/ST=City/L=City/O=Org/OU=Org/CN=cute.client.tests.root-certificate"
# Create the client private key
openssl genrsa -out client.com.key 2048
# Create the client certificate sign request
openssl req -new -key client.com.key -out client.com.csr -subj "/C=CC/ST=City/L=City/O=Org/OU=Org/CN=cute.client.tests.certificate"
# Sign the client certificate using the root certificate and private key
openssl x509 -req -extfile <(printf "subjectAltName=DNS:example.com,IP:127.0.125.10") -in client.com.csr -CA client-root-ca.crt -CAkey client-root-ca.key -CAcreateserial -out client.com.crt -days 3000 -sha256

The client-root-ca.crt certificate is specified in the Cute server's configuration file as shown below:

[Common]
    licenseFilePath=/location/of/license.txt
    logTo = syslog
    logLevel = Info
    remoteObjectsLib = /tmp/fortunes-build/libRemoteObjectsLib.so
    workerCount = 1

[Listener.AllIPV4]
    address = 0.0.0.0
    port = 8440
    certificate = /location/of/server.com.crt
    privateKey = /location/of/server.com.key
    twoWaySSL = true
    caCertificates = /location/of/client-root-ca.crt

And the client.com.crt and client.com.key are added to the client as follows:

QVariant sessionData;
auto sslConfiguration = QSslConfiguration::defaultConfiguration();
QFile trustedCertFile("/location/of/server-root-ca.crt");
trustedCertFile.open(QIODevice::ReadOnly | QIODevice::ExistingOnly);
// Adding the root certificate to the client
auto serverRootCaCert = QSslCertificate::fromData(trustedCertFile.readAll());
sslConfiguration.setCaCertificates(serverRootCaCert);
// Adding client certificate
QFile clientCert("/location/of/client.com.crt");
clientCert.open(QIODevice::ReadOnly | QIODevice::ExistingOnly);
auto clientCertChain = QSslCertificate::fromData(clientCert.readAll());
sslConfiguration.setLocalCertificateChain(clientCertChain);
// Adding private key for client certificate
QFile clientKeyFile("/location/of/client.com.key");
clientKeyFile.open(QIODevice::ReadOnly | QIODevice::ExistingOnly);
QSslKey clientKey(clientKeyFile.readAll(), QSsl::Rsa, QSsl::Pem, QSsl::PrivateKey);
sslConfiguration.setPrivateKey(clientKey);
RemoteObject remoteObject("ClassName",
    QUrl("cutes://example.com:8440/endpoint"),
    sessionData,
    sslConfiguration);

This post shows how to set up encryption on the Cute server. We covered both one-way and two-way SSL authentication.