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 → WebSocket → MQTT → Subscription) 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 full PEM chain (leaf → intermediates → root CA) in the Certificate Wizard; its built-in validator flags any certificate outside its validity window before you connect.
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 Certificate Wizard 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 Wizard. The wizard shows its 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
Open the ALPN list in the Certificate Wizard and add the identifier your broker requires. The AWS IoT guide covers the exact values. For standard MQTT over TLS on 8883, no ALPN entry is usually needed.
Unsupported TLS Version
iOS 15 and later reject TLS 1.0 and 1.1 at the system level. Some hardened brokers require TLS 1.3 and reject older negotiation. Either situation 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 Wizard accepts both and stores 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. The Certificate Wizard validates the key-to-certificate correspondence before saving, surfacing a warning rather than letting a mismatched pair cause a cryptic runtime failure:
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:
| Stage | Fails When |
|---|---|
| DNS | Hostname won’t resolve — typo, VPN routing, split-horizon |
| TCP | Port unreachable — firewall, wrong port, broker down |
| TLS | Handshake error — chain, hostname, ALPN, CA trust |
| WebSocket | HTTP Upgrade fails — wrong path, missing headers |
| MQTT | CONNACK non-zero — credentials, client ID, protocol version |
| Subscription | Subscribe rejected — ACL, wildcard denied |
If the TLS handshake succeeds but the session drops at the MQTT stage, read the CONNACK reason code. Common codes:
| Code | Meaning |
|---|---|
0x87 | Not authorized — ACL or IoT policy denies iot:Connect |
0x86 | Bad username or password |
0x85 | Client identifier not valid |
The Connection Doctor decodes these codes into plain-language messages, and any diagnostic bundle you share has secrets redacted automatically.
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
SANentry (orCN) in the server certificate - Private CA root is imported and trusted
-
ALPNvalue is set if the broker requires it (e.g.,x-amzn-mqtt-cafor AWS IoT on443) - 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
Connectfor 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 Wizard and Connection Doctor built in.