Using mosquitto:
mosquitto_pub -h <public ip address> -t testssl/topic -m "hello world" --cafile ./ca_certificate.pem -p 8883 --tls-version tlsv1.2 -d --id client2
Client client2 sending CONNECT
Client client2 received CONNACK (0)
Client client2 sending PUBLISH (d0, q0, r0, m1, 'testssl/topic', ... (12 bytes))
Client client2 sending DISCONNECT
Using python paho script:
# python 3.11
import datetime
import json
import random
import time
import uuid
from paho.mqtt import client as mqtt_client
from config import broker, port, topic
# Generate a Client ID with the publish prefix.
client_id = f'publish-{random.randint(0, 100)}'
def connect_mqtt():
def on_connect(client, userdata, flags, rc):
if rc == 0:
print("Connected to MQTT Broker!")
else:
print("Failed to connect, return code %d\n", rc)
client = mqtt_client.Client(client_id)
client.tls_set(
ca_certs="ca_certificate.pem",
tls_version=mqtt_client.ssl.PROTOCOL_TLSv1_2,
)
# client.username_pw_set("rw", "readwrite")
client.on_connect = on_connect
client.connect(broker, port)
return client
def fake_sensor_data():
"""Generate a fake sensor data event."""
list_of_sensor_ids = [ uuid.uuid4() for _ in range(10) ]
values = [ 0, 0, 0, 0, 0, 0, 1, 0, 0 ]
return {
"timestamp": time.time(),
"device_id": str(list_of_sensor_ids[random.randint(0, 9)]),
"event": {
"payload": values[random.randint(0, 8)],
}
}
def publish(client):
while True:
time.sleep(3)
msg = json.dumps(fake_sensor_data())
result = client.publish(topic, msg)
status = result[0]
if status == 0:
print()
print(datetime.datetime.now(), msg)
def run():
print("Connecting to MQTT Broker")
print(f"Broker: {broker}")
print(f"Port: {port}")
print(f"Topic: {topic}")
client = connect_mqtt()
print("Connected!")
print("Ready to publish messages!")
client.loop_start()
publish(client)
client.loop_stop()
if __name__ == '__main__':
run()
Error:
ssl.SSLCertVerificationError: [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: IP address mismatch, certificate is not valid for '<public ip address>'. (_ssl.c:1006)
I generated the ca_certificate using tls-gen on my server in a script. Elsewhere I read that if I am providing a public ip address instead of a DNS name, I should be using a SAN, however tls-gen does not allow for me to specify one easily - would be good to not have to modify the server initiation script too much.
I would like to understand why mosquitto would work and python/paho needs a SAN for the same certificate file.
The reason mosquitto verifies the certificate can be found in the mosquitto__verify_certificate_hostname
function in the file lib/tls_mosq.c
:
This can be seen to check the SAN
entries and only checks the CN
if there is no SAN
section in the certificate.
if(have_san_dns){
/* Only check CN if subjectAltName DNS entry does not exist. */
return 0;
}`
So without explicitly asking Roger (mosquitto maintainer) I expect this is just mosquitto still supporting the long deprecated option to use the CN
where as Python has dropped that option.
Either way the best option is to produce a valid certificate with the correct SAN
options (especially if using raw IP addresses)