Authenticating client with SSL/TLS certificate

Time to complete: 10 min.

Overview

From this tutorial, you will learn some additional concepts of the Kaa platform and discover how to:

  • communicate with the platform over one-way and two-way (mutual) MQTT over SSL/TLS
  • authenticate [client][client] (or device) using X.509 certificate
  • suspend/revoke/reactivate client X.509 certificate

One-way SSL/TLS communication

In one-way SSL, it is the task of the client to validate the server and ensure that the received data comes from the right source. After accepting the connection on MQTT over a TLS port (8883 by default), the Kaa server sends its public certificate to the client. Then the client validates the received certificate through either a certification authority (CA) or its own trust store.

Playbook

Let’s play with one-way SSL/TLS communication by connecting an MQTT client to the 8883 port, which serves MQTT over TLS. Do not confuse 8883 with the 1883 port, because, 1883 serves plain, non-encrypted MQTT.

We assume that you have already created an application, application version, and endpoint with a token while following the “connecting your first device” tutorial. You can reuse them or create new ones.

Start by logging into your Kaa Cloud account.

To obtain the Kaa server root certificate, go to the Device management, then Credentials, click Get root certificate, and copy the certificate content.

Get root certificate button

Create a file named ca.pem in your file system and paste the copied root certificate into the file.

To run the below MQTT client on your PC, you will need Python 3 installed. To speed things up a little, you may also just open it on Repl.it, and paste the copied certificate there, into the empty file ca.pem.

The below client was taken from the “Collecting data from a device” tutorial and upgraded to communicate over MQTT over SSL/TLS. Create the client.py file with the below content and place it in the same folder as ca.pem.

# Simple MQTT-based one-way SSL/TLS client for the Kaa IoT platform.
# See https://docs.kaaiot.io/KAA/docs/current/Tutorials/getting-started/authenticating-client-with-tls-certificate/.

import json
import random
import signal
import string
import time

import paho.mqtt.client as mqtt

KPC_HOST = "mqtt.cloud.kaaiot.com"  # Kaa Cloud MQTT host
KPC_PORT = 8883  # Kaa Cloud MQTT over SSL/TLS port

ENDPOINT_TOKEN = ""  # Paste your endpoint token
APPLICATION_VERSION = ""  # Paste your application version


class DataCollectionClient:

    def __init__(self, client):
        self.client = client
        self.data_collection_topic = f'kp1/{APPLICATION_VERSION}/dcx/{ENDPOINT_TOKEN}/json'

    def connect_to_server(self):
        print(
            f'Connecting to Kaa server at {KPC_HOST}:{KPC_PORT} using application version {APPLICATION_VERSION} and endpoint token {ENDPOINT_TOKEN}')
        self.client.connect(KPC_HOST, KPC_PORT, 60)
        print('Successfully connected')

    def disconnect_from_server(self):
        print(f'Disconnecting from Kaa server at {KPC_HOST}:{KPC_PORT}...')
        self.client.loop_stop()
        self.client.disconnect()
        print('Successfully disconnected')

    def compose_data_sample(self):
        return json.dumps([{
            'timestamp': int(round(time.time() * 1000)),
            'temperature': random.randint(15, 25),
            'humidity': random.randint(35, 60),
        }])


def on_message(client, userdata, message):
    print(
        f'<-- Received message on topic "{message.topic}":\n{str(message.payload.decode("utf-8"))}')


def main():
    # Initiate server connection
    client = mqtt.Client(
        client_id=''.join(random.choice(string.ascii_uppercase + string.digits) for _ in range(6)))

    data_collection_client = DataCollectionClient(client)
    data_collection_client.connect_to_server()
    client.on_message = on_message
    client.tls_set(ca_certs="ca.pem")

    # Start the loop
    client.loop_start()

    # Send data samples in loop
    listener = SignalListener()
    while listener.keepRunning:

        payload = data_collection_client.compose_data_sample()

        result = data_collection_client.client.publish(
            topic=data_collection_client.data_collection_topic, payload=payload)
        if result.rc != 0:
            print('Server connection lost, attempting to reconnect')
            data_collection_client.connect_to_server()
        else:
            print(
                f'--> Sent message on topic "{data_collection_client.data_collection_topic}":\n{payload}')

        time.sleep(3)

    data_collection_client.disconnect_from_server()


class SignalListener:
    keepRunning = True

    def __init__(self):
        signal.signal(signal.SIGINT, self.stop)
        signal.signal(signal.SIGTERM, self.stop)

    def stop(self, signum, frame):
        print('Shutting down...')
        self.keepRunning = False


if __name__ == '__main__':
    main()

Now you should have a folder with two files—one with the Kaa server root certificate (ca.pem) and the other with the Python MQTT client (client.py).

One way TLS project structure

Initialize the ENDPOINT_TOKEN variable with the endpoint token, APPLICATION_VERSION with the endpoint application version, and run the client.py Python script. Verify that the telemetry chart on the device dashboard contains data.

One-way TLS client telemetry

Congrats, you have set up one-way SSL/TLS communication! It’s time to upgrade your skills and master two-way SSL/TLS communication, which is even more secure.


Two-way (mutual) SSL/TLS communication

In the case with two-way SSL, both the client and the server authenticate each other to ensure that both parties involved in the communication can be trusted. Both parties share their public certificates with each other and then perform mutual verification/validation. With the mutual SSL/TLS communication feature enabled, the Kaa server starts data exchange only with those clients that presented a Kaa-trusted certificate.

Playbook

To enable the mutual SSL/TLS communication feature, go to the Device management, then Credentials, then the TLS client certificates tab, click shield icon, and turn on the TLS client certificate authentication for the MQTT/TLS transport.

Enable mutual TLS

Now that the feature is enabled, Kaa immediately drops connections with those clients that start data exchange without presenting a trusted X.509 certificate.

To generate a Kaa-trusted X.509 certificate/key pair, click Add TLS client certificate, enter optional Common Name (CN) and TTL, and click Create.

Generate certificate/key pair

The pop-up with a certificate chain and a private key must appear.

Client certificate/key pair pop-up

Create the file named client.crt in your file system and add there the content of the Certificate chain field from the pop-up. Then, create the file named client.key and add there the content of the Private key field from the pop-up.

We assume that you have already created the ca.pem with the Kaa server root certificate as described above in the one-way SSL/TLS communication section, so create it if you haven’t done it yet.

To run the below MQTT client on your PC, you will need Python 3 installed. To speed things up a little, you may also just open it on Repl.it, and paste the copied server root certificate, client certificate, and private key there into empty files named ca.pem, client.crt and client.key, respectively.

Create the client.py file with the below content and place it in the same folder as ca.pem, client.crt and client.key.

# Simple MQTT-based one-way SSL/TLS client for the Kaa IoT platform.
# See https://docs.kaaiot.io/KAA/docs/current/Tutorials/getting-started/tls-certificate-client-communication/.

import json
import random
import signal
import string
import time

import paho.mqtt.client as mqtt

KPC_HOST = "mqtt.cloud.kaaiot.com"  # Kaa Cloud plain MQTT host
KPC_PORT = 8883  # Kaa Cloud plain MQTT port

ENDPOINT_TOKEN = ""  # Paste your endpoint token
APPLICATION_VERSION = ""  # Paste your application version


class DataCollectionClient:

    def __init__(self, client):
        self.client = client
        self.data_collection_topic = f'kp1/{APPLICATION_VERSION}/dcx/{ENDPOINT_TOKEN}/json'

    def connect_to_server(self):
        print(
            f'Connecting to Kaa server at {KPC_HOST}:{KPC_PORT} using application version {APPLICATION_VERSION} and endpoint token {ENDPOINT_TOKEN}')
        self.client.connect(KPC_HOST, KPC_PORT, 60)
        print('Successfully connected')

    def disconnect_from_server(self):
        print(f'Disconnecting from Kaa server at {KPC_HOST}:{KPC_PORT}...')
        self.client.loop_stop()
        self.client.disconnect()
        print('Successfully disconnected')

    def compose_data_sample(self):
        return json.dumps([{
            'timestamp': int(round(time.time() * 1000)),
            'temperature': random.randint(15, 25),
            'humidity': random.randint(35, 60),
        }])


def on_message(client, userdata, message):
    print(
        f'<-- Received message on topic "{message.topic}":\n{str(message.payload.decode("utf-8"))}')


def main():
    # Initiate server connection
    client = mqtt.Client(
        client_id=''.join(random.choice(string.ascii_uppercase + string.digits) for _ in range(6)))

    data_collection_client = DataCollectionClient(client)
    data_collection_client.connect_to_server()
    client.on_message = on_message
    client.tls_set(ca_certs="ca.pem", certfile="client.crt", keyfile="client.key")

    # Start the loop
    client.loop_start()

    # Send data samples in loop
    listener = SignalListener()
    while listener.keepRunning:

        payload = data_collection_client.compose_data_sample()

        result = data_collection_client.client.publish(
            topic=data_collection_client.data_collection_topic, payload=payload)
        if result.rc != 0:
            print('Server connection lost, attempting to reconnect')
            data_collection_client.connect_to_server()
        else:
            print(
                f'--> Sent message on topic "{data_collection_client.data_collection_topic}":\n{payload}')

        time.sleep(3)

    data_collection_client.disconnect_from_server()


class SignalListener:
    keepRunning = True

    def __init__(self):
        signal.signal(signal.SIGINT, self.stop)
        signal.signal(signal.SIGTERM, self.stop)

    def stop(self, signum, frame):
        print('Shutting down...')
        self.keepRunning = False


if __name__ == '__main__':
    main()

Now you should have a folder with four files:

  • Kaa server root certificate - ca.pem
  • client certificate - client.crt
  • client private key - client.key
  • Python MQTT client - client.py

Two way TLS project structure

Initialize the ENDPOINT_TOKEN variable with the endpoint token, APPLICATION_VERSION with the endpoint application version, and run the client.py Python script. Verify that the telemetry chart on the device dashboard contains data.

Two-way TLS client telemetry

TLS client certificate state management

Client X.509 (TLS) certificates can have one of the below states:

  • Inactive is the initial state for a newly provisioned certificate that has not yet been used to authenticate a client.
  • Active is the state to which the certificate automatically moves after it was first used for the client authentication. A certificate can be suspended or revoked from the active state.
  • Suspended state is for a temporarily disabled certificate. Kaa rejects authentication requests with a suspended certificate. A suspended certificate can be re-activated.
  • Revoked state is the terminal state for a certificate that is no longer valid.

Go to the Device management, then Credentials, find the recently generated certificate that is used by the running MQTT client, and suspend it.

Suspend certificate

Check the running MQTT client, it must be disconnected. Note that the platform automatically disconnects all clients that were using a certificate in the moment of its suspension or revocation.