Skip to main content

TLS in Python for MQTT

Use TLS in Python for MQTT

Abstract

Use TLS in Python for MQTT

If you are familiar with the Python Paho MQTT library, implementing TLS is simple.

Prerequisites
  • Ensure you have properly enabled TLS and created and configured TLS certificates. See Secure MQTT with certificates.

    You must create a separate server certificate for each computer that runs a FairCom server. Each FairCom server must be configured to use the appropriate server certificate for the computer on which it runs

  • Each user must have a unique client certificate. A client certificate allows a client program to identify itself to a FairCom server. Each program that wants to use a certificate must be configured to send the certificate to the server.

    Note

    Do not use a client certificate in a client program to connect to a FairCom server that is not configured with a certificate.

Ensure you meet the prerequisites before this procedure.

  1. Configure the MQTTS listener in the services.json file to use your CA certificate, server certificate, and private key.

  2. Ensure the "enabled" property is set to true.

  3. Update the "tls" object:

    • Update the "certificateFilename" property value to the certificate you obtained for this FairCom server.

    • Add a "privateKeyFilename" property and set it to the private key for this FairCom server.

    • Add a "certificateAuthoritiesFilename" property and set it to the path and filename containing CA certificate.

      Note

      This is the certificate from the CA that signed the server certificate.

  4. Save any changes.

  5. Restart the server for the changes to take effect.

Example 1. MQTTS listener
{
  "serviceName": "mqtts8883",
  "description": "Port 8883 using TLS-secured MQTTS protocol for the MQTT broker on all TCP/IP addresses bound to this server",
  "port": 8883,
  "protocol": "mqtts",
  "enabled": true,
  "tls": {
    "certificateAuthoritiesFilename": "C:/Certificates/ca.crt",
    "certificateFilename":            "C:/Certificates/server.crt",
    "privateKeyFilename":             "C:/Certificates/server.key"
  }
},


Example 2. MQTTWSS listener
{
  "serviceName": "mqttwss9002",
  "description": "Port 9002 using TLS-secured WebSocket protocol for the MQTT broker on all TCP/IP addresses bound to this server",
  "port": 9002,
  "protocol": "mqttwss",
  "enabled": true,
  "tls": {
    "certificateAuthoritiesFilename": "C:/Certificates/ca.crt",
    "certificateFilename":            "C:/Certificates/server.crt",
    "privateKeyFilename":             "C:/Certificates/server.key"
  }
}


Ensure you meet the prerequisites before this procedure.

Make the following updates to your MQTT code:
  1. Update the "port" property value to the TLS port used by your broker.

  2. Call tls_set() on your paho.mqtt.client class object with the following parameters (see Example program):

    1. ca_certs

    2. certfile

    3. keyfile

Ensure you meet the prerequisites before this procedure.

Build an MQTT project:
  1. Create a callback function that will print any messages received using the following code:

    def message_callback( message_client, userdata, message ):
      print( message.payload.decode( 'utf8' ) )
  2. Define a client using the following code:

    Note

    To use a secure WebSocket, this call only needs the additional parameter of transport = 'websockets'.

    mqtts_client = mqtt.Client( client_id = "MQTTS Client ID" )
  3. Enable TLS by calling tls_set() on the newly created client using the following code:

    mqtts_client.tls_set( ca_certs = "/Certificates/ca.crt", 
                          certfile = "/Certificates/client.crt", 
                          keyfile = "/Certificates/client.key" )
  4. Assign the message callback to the client using the following code:

    Note

    There are many callbacks in the Paho library, including on_connect, on_connect_fail, on_disconnect, on_message, on_publish, on_subscribe, on_unsubscribe, on_log, on_socket_open, on_socket_close, on_socket_register_write, on_socket_unregister_write.

    mqtts_client.on_message = message_callback
  5. Call loop_start() on the client to begin processing MQTT events using the following code:

    mqtts_client.loop_start()
  6. Connect the client and server using the following code:

    Note

    The port is set to 8883. This must match the port configured in services.json. The broker address must also match the address in its certificate.

    mqtts_client.connect( "MyServer.local", port = 8883 )
  7. Wait a few seconds or use the on_connect callback as a guide to when the connection has been made.

    Note

    The connection will appear instantaneously, but work is happening in the background.

  8. Subscribe to a topic of your choice using the following code:

    mqtts_client.subscribe( "test/MqttsTopic" )
  9. Publish to your chosen topic using the following code:

    mqtts_client.publish( "test/MqttsTopic", "Simple MQTTS message" )
  10. Implement a simple non-blocking delay using the following code:

    Note

    Do this step because even though the message will arrive in a few milliseconds, your code could finish before the message arrives.

    count = 0while count < 5:  time.sleep( 1 )  count += 1
  11. Observe the message:

    Note

    Receiving the message means your client was able to subscribe and publish to your broker using TLS for encryption and authentication.

    Simple MQTTS message
  12. Close the connection using the following code:

    mqtts_client.unsubscribe( "test/MqttsTopic" )
    mqtts_client.disconnect()
    mqtts_client.loop_stop()
    
import time
import paho.mqtt.client as mqtt  # If this fails, do "pip install paho-mqtt"
import ssl

use_client_certificate = True  # Change to False to log in to MQTT broker using a client username/password.
ca_certificate_file = "ca.crt" # File that holds the CA certificate which signed the server certificate used by the broker and the client certificate used by this script.

# These two are for logging into the broker with a client certificate.
client_certificate_file = "user1Client.pem"  # If this also contains the client's private key, set 'client_key_file' to None.
client_key_file = None  # If 'client_certificate_file' does not contain the private key, specify the private key file here.  Otherwise, use None.
client_key_file_password = None  # password to use if client's private key is encrypted.  Otherwise, use None.  If the key is encrypted and this is None, you will be asked for the password at runtime.

# These two are for logging into the broker with a username/password, instead of a client certificate.
client_username = "user1"
client_password = "pass1"


# Make some callback functions which set global status variables.

connected=subscribed=published=False

def on_connect( connect_client, userdata, flags, connect_reason_code, connect_properties = None ):  # 'I connected to the MQTT broker' callback
  global connected
  print( "Connected!" )
  connected = True

def on_disconnect( disconnect_client, userdata, rc ):  # 'I lost my connection to the MQTT broker' callback
  global connected
  print( f"client disconnected with reason code {rc}" )
  connected = False

def on_subscribe( sub_client, userdata, mid, granted_qos ):  # 'I subscribed' callback
  global subscribed
  print( f"subscribed with qos={granted_qos}\n" )
  subscribed = True

def on_unsubscribe( sub_client, userdata, mid ):  # 'I unsubscribed' callback
  global subscribed
  print( "unsubscribed\n" )
  subscribed = False

def on_message( message_client, userdata, message ):  # 'message arrived' callback
  print( "message received: ", str( message.payload.decode( "utf-8" ) ) )

def on_publish( pub_client, userdata, mid ):  # 'I published message' callback
  global published
  print( f"data published mid={mid}\n" )
  published = True


if __name__ == "__main__":
  mqtts_client = mqtt.Client( client_id = "MQTTS Client ID" )
  topic_name = "My Test Topic"


  if use_client_certificate:
    # Connect to the MQTT broker using a username/password
    mqtts_client.tls_set( ca_certs = ca_certificate_file, 
                          certfile = client_certificate_file,
                          keyfile = client_key_file,
                          keyfile_password=client_key_file_password,
                          tls_version=ssl.PROTOCOL_TLSv1_2)
    mqtts_client.tls_insecure_set(False)

  else:  
    # Connect to the MQTT broker using a username/password
    mqtts_client.tls_set( ca_certs = ca_certificate_file, tls_version=ssl.PROTOCOL_TLSv1_2)
    mqtts_client.tls_insecure_set(False)
    mqtts_client.username_pw_set(client_username, client_password) # specify the username and password

  
  # Hook up the callback functions
  mqtts_client.on_connect = on_connect
  mqtts_client.on_disconnect = on_disconnect
  mqtts_client.on_publish = on_publish
  mqtts_client.on_subscribe = on_subscribe
  mqtts_client.on_unsubscribe = on_unsubscribe
  mqtts_client.on_message = on_message

  # Connect to the MQTT broker and start Paho's event loop
  print("Connecting to the MQTT broker...")
  mqtts_client.connect( "localhost", port = 8883 )
  mqtts_client.loop_start()

  while not connected:  # Wait until the connection completes
    time.sleep( 0.5 )

  print("Subscribing to topic...")
  mqtts_client.subscribe( topic_name )
  while not subscribed:  # Wait until the subscription completes
    time.sleep( 0.5 )

  print("Publishing message to topic...")
  mqtts_client.publish( topic_name, "Simple MQTT test message" )
  while not published:  # Wait until the publish completes
    time.sleep( 0.1 )

  for i in range(5): # Wait 5 seconds for the message to arrive
    time.sleep( 1 )
    
  print("Unsubscribing from the topic...")
  mqtts_client.unsubscribe( topic_name )
  while subscribed:  # Wait until the un-subscription completes
    time.sleep( 0.5 )

  print("Disconnecting from the MQTT broker...")
  mqtts_client.disconnect()
  while connected:  # Wait until the disconnect completes
    time.sleep( 0.5 )
  
  mqtts_client.loop_stop()
  
  print("Done!")
  
import time
import paho.mqtt.client as mqtt


def message_callback( message_client, userdata, message ):
  print( message.payload.decode( 'utf8' ) )


if __name__ == "__main__":
  mqtts_client = mqtt.Client( client_id = "MQTTS Client ID", transport = 'websockets' )
  mqtts_client.tls_set( ca_certs = "/Certificates/ca.crt",
                        certfile = "/Certificates/client.crt",
                        keyfile = "/Certificates/client.key" )
  mqtts_client.on_message = message_callback
  mqtts_client.loop_start()

  mqtts_client.connect( "MyServer.local", port = 9002 )
  time.sleep( 2 )

  mqtts_client.subscribe( "test/MqttWssTopic" )
  mqtts_client.publish( "test/MqttWssTopic", "Simple MQTT WSS message" )

  count = 0
  while count < 5:
    time.sleep( 1 )
    count += 1
  mqtts_client.unsubscribe( "test/MqttWssTopic" )
  mqtts_client.disconnect()
  mqtts_client.loop_stop()