Blog

Debugging MQTT TLS and mTLS Connections

Why secure MQTT connections fail and how to fix them: expired chains, hostname mismatches, wrong ALPN, and mutual-TLS errors — diagnosed stage by stage on iOS.

May 21, 2026

How a Secure MQTT Connection Is Established

Three distinct layers must succeed in order before a single MQTT message can flow.

TCP. The client resolves the broker hostname, opens a socket on port 8883 (MQTT/TLS) or 443/8084 (WebSocket/TLS), and completes the three-way handshake. Failures here are network issues, not TLS or MQTT issues.

TLS handshake. With a socket open, client and server negotiate a cipher suite, exchange certificates, and establish an encrypted channel. This is where most secure-MQTT failures occur: certificate chain validation, hostname verification, ALPN negotiation, and in mTLS setups, client certificate authentication.

MQTT CONNECT. Only after TLS succeeds does the MQTT protocol begin. The client sends CONNECT; the broker replies with CONNACK. A non-zero reason code here is an application-level rejection — credentials wrong, authorization denied, client ID invalid — not a TLS problem.

Pinning down which layer failed determines the right fix. The Connection Doctor runs staged diagnostics (DNS → TCP → TLS → certificate chain → hostname → ALPN/SNI → MQTT CONNACK → auth) and names the failing stage with a plain-language cause and remedy.


Common TLS Failures

Expired or Incomplete Certificate Chain

The broker’s leaf certificate has an expiry date, and so does every intermediate CA. An expired intermediate breaks connections even when the leaf cert is current.

TLS handshake error: certificate has expired or is not yet valid
TLS handshake error: unable to verify the first certificate

Import the broker’s CA certificate (PEM) in the Certificate Manager; its built-in validator flags an expired certificate before you connect. If the broker sends an incomplete chain, it must be fixed on the broker — the client validates against the CA you trust, not a chain you supply.

Hostname / SAN Mismatch

TLS clients verify that the connected hostname matches a SAN (Subject Alternative Name) entry in the server certificate, or the CN field as a fallback. An exact match is required.

TLS handshake error: hostname "broker.example.com" does not match CN "mqtt.example.com"

Connect using the exact hostname in the SAN, or use the SNI override in the connection profile’s TLS settings to send the correct server name during the handshake without changing your connection address.

Untrusted or Self-Signed CA

Private brokers — Mosquitto on a Raspberry Pi, internal test environments, AWS IoT custom endpoints — often use certificates signed by a private CA that iOS doesn’t trust by default.

TLS handshake error: self signed certificate in certificate chain

Import your private CA’s root certificate as a PEM file in the Certificate Manager. It shows the certificate’s SHA-256 fingerprint so you can verify you’re trusting the right authority. Certificate material is stored in the iOS Keychain, not in app storage.

Wrong or Missing ALPN

Some brokers use ALPN to route MQTT on shared ports. AWS IoT Core accepts MQTT on port 443 only when the client advertises x-amzn-mqtt-ca in the ALPN extension. Omitting it produces a TLS alert that resembles a certificate failure.

TLS handshake error: tlsv1 alert internal error

Set the ALPN protocol in the connection profile’s TLS settings and add the identifier your broker requires; an AWS IoT toggle fills in x-amzn-mqtt-ca for you. The AWS IoT guide covers the exact values. For standard MQTT over TLS on 8883, no ALPN entry is usually needed.

Unsupported TLS Version

Modern iOS effectively treats TLS 1.0 and 1.1 as obsolete, and MQTT Commander negotiates TLS 1.2 or 1.3. Conversely, some hardened brokers require TLS 1.3 and reject older negotiation. Either mismatch produces:

TLS handshake error: unsupported protocol

This is a server-side configuration issue. Update the broker to accept TLS 1.2 or 1.3.


Mutual TLS (mTLS): Client Certificate Authentication

Standard TLS only authenticates the server. Mutual TLS requires both parties to present and verify certificates — common in AWS IoT Core device provisioning, industrial IoT, and enterprise deployments.

What You Need

  • Client certificate — issued to your device or service account
  • Client private key — the private key corresponding to the client certificate
  • CA certificate — the authority that signed both the client and server certificates

These arrive as a .p12/.pfx bundle (certificate + key, passphrase-protected) or as separate PEM files. The Certificate Manager imports the client identity from a .p12/.pfx bundle and the CA trust anchor from a PEM file, storing everything in the iOS Keychain.

Common mTLS Errors

Passphrase mismatch on a .p12 bundle. Import fails with “incorrect passphrase.” Passphrases are case-sensitive; re-export from your CA if you’ve lost the correct value.

Client cert and private key don’t match. Importing the client identity as a single .p12/.pfx bundle keeps the certificate and its key paired by construction, which avoids this class of mismatch. A pair that still doesn’t match the broker’s expectations surfaces at handshake time:

TLS handshake error: private key does not match public key

Handshake succeeds but broker disconnects immediately. This is a broker-side authorization failure. In AWS IoT Core it typically means the certificate’s Thing Policy doesn’t grant iot:Connect for the client ID being presented. Inspect the CONNACK reason code — 0x87 means “not authorized.”


Staged Diagnostics and CONNACK Reason Codes

The Connection Doctor stops at the first failing stage and reports exactly what went wrong:

StageFails When
DNSHostname won’t resolve — typo, VPN routing, split-horizon
TCPPort unreachable — firewall, wrong port, broker down (WebSocket/WSS upgrade failures surface here too)
TLSHandshake error — untrusted CA, expired or not-yet-valid cert
Certificate hostnameConnected host isn’t in the certificate’s SAN/CN
ALPN / SNIWrong or missing ALPN identifier, or SNI name mismatch
MQTT CONNACKNon-zero reason code — credentials, client ID, protocol version
AuthBroker rejects the credentials or client certificate

If the TLS handshake succeeds but the session drops at the MQTT stage, read the CONNACK reason code. Common codes:

CodeMeaning
0x87Not authorized — ACL or IoT policy denies iot:Connect
0x86Bad username or password
0x85Client identifier not valid

The Connection Doctor decodes these codes into plain-language messages. Secrets, raw payloads, and private hostnames never reach the diagnostic log, so a result is safe to paste into a ticket.


Practical Checklist

  • Port is correct: 8883 (MQTT/TLS), 8084 (MQTT/WSS), 443 (ALPN routing)
  • Server certificate chain is complete and all certs are unexpired
  • Connection hostname matches a SAN entry (or CN) in the server certificate
  • Private CA root is imported and trusted
  • ALPN value is set if the broker requires it (e.g., x-amzn-mqtt-ca for AWS IoT on 443)
  • For mTLS: client certificate, private key, and CA are all present; passphrase is correct
  • Client cert private key matches the certificate’s public key
  • Broker policy grants Connect for this client ID

For the full connection workflow on iOS, see the MQTT on iPhone guide. MQTT Commander for iOS is a $2.99 one-time purchase with the Certificate Manager and Connection Doctor built in.

← All guides

Debug MQTT on your iPhone

MQTT Commander brings this workflow to iOS and iPadOS.

$2.99 · one-time purchase