MQTT mTLS (mutual TLS) authenticates both sides of a connection with certificates: the broker proves its identity to the device, and the device proves its identity to the broker with its own X.509 client certificate. This article explains how that works, shows a complete Mosquitto setup with OpenSSL, and helps you decide whether mTLS is worth its operational cost or whether username and password over TLS is the better choice for your fleet.
Server TLS vs Mutual TLS
In ordinary MQTT over TLS, only the broker presents a certificate. The client verifies it, the tunnel is encrypted, and then the client authenticates inside that tunnel with the username and password fields of the MQTT CONNECT packet. This is how most cloud brokers work, and it is explained in detail in our MQTT TLS configuration guide.
With mutual TLS, the broker also sends a CertificateRequest during the handshake. The client answers with its own certificate and signs part of the handshake with the matching private key. The broker checks that the client certificate chains to a CA it trusts, is within its validity period, and has not been revoked. If any check fails, the TCP connection is closed before a single MQTT packet is processed.
| Aspect | Credentials over TLS | Mutual TLS (mTLS) |
|---|---|---|
| Device secret | Username and password | Private key + certificate |
| Secret sent over network | Yes (inside encrypted tunnel) | No, only a signature |
| Rejected unauthenticated clients | After MQTT CONNECT | During the TLS handshake |
| Rotation | Change a password | Re-issue and deploy a certificate |
| Revocation | Disable the credential | CRL or OCSP, or short certificate lifetimes |
| Infrastructure needed | Broker user store | A CA, issuance process, renewal process |
| Hardware key protection | Possible but uncommon | Natural fit (secure elements, TPMs) |
Why Teams Choose MQTT mTLS
The strongest argument for mTLS is that the device secret never leaves the device. A password is transmitted on every connect, so a compromised broker, a misconfigured log, or a debugging proxy can capture it. A private key only produces signatures. If the key lives in a secure element such as an ATECC608 or a TPM, it cannot even be read out by firmware, which makes cloning a device much harder.
Other benefits:
- Identity bound to hardware: you can issue certificates at the factory and tie the certificate's common name to the serial number.
- Early rejection: unauthenticated clients are dropped before the broker parses MQTT, which reduces attack surface.
- Standard tooling: X.509 is understood by every TLS library, load balancer and security team.
The Hidden Cost: Certificate Lifecycle
mTLS shifts complexity from the broker's user database to a public key infrastructure. Before adopting it, be honest about these tasks:
- Run a CA: keep the root key offline, use an intermediate for issuing, and protect both.
- Provision: generate a key on each device (ideally on the device itself), create a CSR, sign it, and install the certificate.
- Renew: certificates expire. Devices need a way to obtain a new certificate before the old one lapses, even if they were on a shelf for a year.
- Revoke: when a device is stolen or decommissioned, the broker must stop accepting its certificate. Mosquitto supports a CRL file; many teams prefer short certificate lifetimes instead.
- Rotate the CA: eventually the issuing CA itself must be replaced, which means distributing a new trust anchor to the broker and new certificates to every device.
Get any of these wrong and you get outages that look like mysterious handshake failures across part of the fleet. For most small and medium projects, unique per-device credentials over TLS, rotated on a schedule, offer a better security-to-effort ratio. Our guides to MQTT username and password authentication and rotating MQTT credentials show how to do that well.
Setting Up MQTT mTLS on Mosquitto
The following lab setup uses Eclipse Mosquitto, which supports client certificate authentication natively. Run it on a machine you control.
Step 1: Create a Private CA
mkdir -p ~/mqtt-pki && cd ~/mqtt-pki
# CA key and self-signed CA certificate (10 years)
openssl genrsa -out ca.key 4096
openssl req -x509 -new -key ca.key -sha256 -days 3650 \
-subj "/CN=Lab MQTT Root CA" -out ca.crt
Step 2: Issue the Broker Certificate
The broker certificate needs a Subject Alternative Name matching the hostname clients will use.
openssl genrsa -out server.key 2048
openssl req -new -key server.key -subj "/CN=mqtt.lab.local" -out server.csr
cat > server.ext <<'EOF'
subjectAltName = DNS:mqtt.lab.local
extendedKeyUsage = serverAuth
EOF
openssl x509 -req -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial \
-days 825 -sha256 -extfile server.ext -out server.crt
Step 3: Issue a Device Certificate
Use the device ID as the common name. In production the key would be generated on the device and only the CSR would leave it.
openssl genrsa -out sensor-001.key 2048
openssl req -new -key sensor-001.key -subj "/CN=sensor-001" -out sensor-001.csr
cat > client.ext <<'EOF'
extendedKeyUsage = clientAuth
EOF
openssl x509 -req -in sensor-001.csr -CA ca.crt -CAkey ca.key -CAcreateserial \
-days 365 -sha256 -extfile client.ext -out sensor-001.crt
Step 4: Configure the Broker
# /etc/mosquitto/conf.d/mtls.conf
listener 8883
cafile /etc/mosquitto/certs/ca.crt
certfile /etc/mosquitto/certs/server.crt
keyfile /etc/mosquitto/certs/server.key
# Require a client certificate signed by cafile
require_certificate true
# Use the certificate CN as the MQTT username (for ACLs)
use_identity_as_username true
# Optional: reject revoked certificates
# crlfile /etc/mosquitto/certs/ca.crl
Copy the files into /etc/mosquitto/certs/, make the key readable by the mosquitto user, and restart the service. With use_identity_as_username, the CN sensor-001 becomes the username, so you can write ACL rules per device.
Step 5: Connect With the Client Certificate
mosquitto_sub -h mqtt.lab.local -p 8883 --cafile ca.crt \
--cert sensor-001.crt --key sensor-001.key \
-i sensor-001 -t "lab/sensor-001/#" -v
The same three files work in code. In Python with paho-mqtt 2.x:
import paho.mqtt.client as mqtt
client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2, client_id="sensor-001")
client.tls_set(ca_certs="ca.crt", certfile="sensor-001.crt", keyfile="sensor-001.key")
client.connect("mqtt.lab.local", 8883)
client.publish("lab/sensor-001/temp", "21.4", qos=1)
client.loop(2)
client.disconnect()
If you connect without --cert and --key, the handshake fails immediately, which is a quick way to confirm require_certificate is active. Lab setups like this one use a self-signed root; our guide to self-signed certificates for MQTT covers the pitfalls.
Common mTLS Errors
- "certificate required" or handshake alert from the broker: the client did not send a certificate. Check the cert and key paths.
- "unknown ca": the client certificate was signed by a CA that is not in the broker's
cafile. - "unsupported certificate purpose": the certificate lacks the
clientAuthextended key usage. - Key mismatch: the key file does not belong to the certificate. Compare
openssl x509 -noout -modulus -in sensor-001.crt | openssl md5with the same command on the key. - Expired certificate: check dates with
openssl x509 -noout -dates -in sensor-001.crt, and check the device clock.
When to Use mTLS and When Credentials Are Enough
mTLS is a good fit when you control the broker, need hardware-backed identity, operate in a regulated environment that mandates certificate-based device identity, or already run a PKI. It is usually overkill for prototypes, small fleets, and teams that have no one to own certificate renewal.
ConnectMQ takes the credentials-over-TLS approach: the broker presents a publicly trusted Let's Encrypt certificate, and each client authenticates with a username, password and client ID generated in the portal. Client IDs are bound to the issued credentials, and repeated failed logins lead to IP blocking. ConnectMQ does not offer client-certificate authentication, so if mTLS is a hard requirement you will need a broker you configure yourself, such as Mosquitto as shown above. For the majority of projects, though, a fully managed MQTT service with strong per-device credentials removes far more operational risk than mTLS adds. The broader picture is covered in our MQTT security best practices.
Get Started with ConnectMQ
If credentials over TLS fit your threat model, ConnectMQ gives you a secured broker without running any PKI. Sign up at app.connectmq.com, pick the free Serverless plan with no credit card, create a broker, and connect on port 8883 with the generated credentials. You can prototype first on the public MQTT test broker at broker.connectmq.com, then switch to private credentials when you go live.
Frequently Asked Questions
Is mTLS more secure than username and password over TLS?
It removes the shared secret from the wire and pairs well with hardware key storage, so it resists credential theft better. The trade-off is a certificate lifecycle you must operate reliably.
Can I use mTLS and a username and password together?
Yes. Mosquitto, for example, can require a client certificate and still check the MQTT username and password, giving two independent factors.
Does ConnectMQ support client certificate authentication?
No. ConnectMQ authenticates clients with a generated username, password and client ID sent over TLS. Use a self-managed broker if you specifically need mTLS.
How long should device certificates be valid?
There is no single answer. Shorter lifetimes reduce reliance on revocation but require dependable automated renewal; choose the shortest period your renewal process can handle without risk of expiry in the field.



