Debug MQTT TLS and mTLS Connections on iOS

A generic “handshake failed” error hides a dozen possible root causes. MQTT Commander’s staged diagnostics walk through DNS, TCP, TLS, and MQTT handshakes one layer at a time—naming the exact failing stage in plain language so you fix the right problem on the first attempt.

$2.99 · one-time purchase

Debug MQTT TLS and mTLS Connections on iOS
TLS + mTLS
Full mutual-TLS support with client certificate authentication
Expiry & SAN
Checks certificate expiry, CN, and Subject Alternative Names against the hostname
CONNACK codes
Decodes every MQTT 5 CONNACK reason code to a plain-language fix
$2.99 once
One-time purchase, no subscription, works on iOS 15+ iPhone and iPad

Who it's for

  • Embedded and IoT engineers testing broker TLS configuration before deployment
  • Backend developers diagnosing AWS IoT Core, HiveMQ, Mosquitto, or EMQX TLS issues from an iPhone
  • Security engineers validating mutual-TLS client-certificate flows end-to-end
  • QA teams reproducing intermittent TLS failures that only surface on real mobile devices
  • Makers and hobbyists whose self-signed CA broker refuses to connect

Why MQTT TLS Connections Fail—and How to Tell Them Apart

MQTT over TLS fails at several distinct layers, each with its own symptoms. An expired certificate or incomplete chain causes the TLS handshake to abort before a single MQTT byte is sent. A hostname or SAN mismatch fails identically at the TLS layer but for a different reason—the broker’s certificate does not cover the hostname or IP you dialed. An untrusted or self-signed CA fails because the client refuses to anchor trust. Wrong or absent ALPN protocol negotiation causes AWS IoT Core and other load-balanced brokers to reject the connection silently after TLS completes. An unsupported TLS version (e.g., a broker that requires TLS 1.2 when the client tries 1.3, or vice versa) causes a protocol_version alert. Because these errors all present as a single opaque connection failure in most clients, the only reliable way to isolate the layer is staged diagnostics—exactly what Connection Doctor provides.

Staged Diagnostics: Isolating the Failing Layer

Connection Doctor runs through DNS resolution, TCP reachability, TLS handshake, optional mTLS client-certificate exchange, and MQTT CONNECT/CONNACK in sequence. Each stage either passes or stops with a typed, plain-language diagnosis—for example “TLS handshake failed: server certificate is not trusted. The broker’s CA is not in the system trust store and no custom CA was imported.” No more correlating cryptic errSSL* codes against broker logs by hand. Secrets, raw payloads, and private hostnames never reach the log, so a result is safe to share for remote support. Learn the full methodology in the TLS debugging guide.

Certificate Manager: Import Once, Reuse Across Connections

Certificate Manager accepts a .p12/.pfx bundle (with passphrase) for the client identity—the client certificate and its private key travel together, since a standalone PEM private key is not supported—while the CA certificate is imported as a PEM. After import it immediately checks the certificate’s expiry and confirms that the Subject Alternative Names or Common Name cover the hostname you are connecting to; trust is anchored to the CA you import. SHA-256 fingerprints are displayed so you can cross-check against the broker. For mutual TLS it stores both the CA trust anchor and the client identity; all key material is kept in the iOS Keychain and never leaves the device. ALPN configuration—including x-amzn-mqtt-ca for AWS IoT Core port 443—and SNI override are set per-connection without touching the certificate itself.

Mutual TLS (mTLS) Step by Step

mTLS requires the broker to trust your client certificate and the client to trust the broker’s CA. A common mistake is importing the client certificate without importing the CA, or importing the CA to the wrong profile. Certificate Manager keeps them explicitly separate so each role is unambiguous. Once both are imported, Connection Doctor’s staged run will show TLS completing successfully and then report any MQTT-layer rejection (bad credentials, ACL, etc.) as a separate, clearly labeled stage. This separation is what makes mTLS debugging tractable: you know the TLS handshake succeeded before you start investigating broker-side authorization. MQTT Commander supports mTLS on TCP-with-TLS (mqtts://), WebSocket-with-TLS (wss://), and all MQTT 3.1.1 and MQTT 5 variants.

Reading MQTT 5 CONNACK Reason Codes After TLS Succeeds

Once the TLS layer is healthy, broker-side rejections surface as MQTT CONNACK reason codes. MQTT 5 defines over two dozen codes; the most common in TLS deployments are 0x87 (Not Authorized—often an mTLS client-certificate not mapped to an ACL policy), 0x85 (Client Identifier Not Valid), and 0x8D (Keep Alive Timeout). Connection Doctor decodes each code to a human-readable description and suggests the most likely fix—so you never need to cross-reference the specification manually. For MQTT 3.1.1 brokers the tool maps the equivalent numeric return codes (0–5) to the same plain-language output.

Frequently asked questions

Why does my MQTT TLS connection fail with a generic error and no details?

Most MQTT clients surface TLS failures as a single undifferentiated error because the underlying TLS alert is swallowed. The root cause is almost always one of five things: an expired or incomplete certificate chain, a hostname or SAN mismatch, an untrusted CA (including self-signed CAs), a missing or wrong ALPN protocol string, or a TLS version mismatch. Connection Doctor runs staged diagnostics that identify which of these layers failed and names the specific cause in plain language.

How do I import a self-signed CA so MQTT Commander trusts my broker?

Open Certificate Manager, tap Add Certificate, choose CA Certificate, and import your PEM file. It validates the certificate and shows its SHA-256 fingerprint so you can confirm it matches the one on your broker. Once imported, assign it to your connection profile. Connection Doctor will then anchor TLS trust to your CA instead of the system trust store.

What ALPN value do I need for AWS IoT Core on port 443?

Set the ALPN protocol string to x-amzn-mqtt-ca. AWS IoT Core uses port 8883 for standard MQTT TLS and port 443 with ALPN x-amzn-mqtt-ca so traffic can pass through corporate firewalls that block 8883. In MQTT Commander, set ALPN in the connection profile TLS settings. Certificate Manager validates that mTLS is also configured, since AWS IoT Core requires a device certificate for authentication.

Does MQTT Commander support mutual TLS (mTLS) with client certificates?

Yes. Certificate Manager imports your client certificate and private key together as a .p12/.pfx bundle (a standalone PEM private key is not supported), and your broker CA as a separate PEM. All key material is stored in the iOS Keychain. Connection Doctor verifies the full mTLS handshake in its TLS stage and clearly distinguishes TLS handshake success from any subsequent MQTT-layer authorization failure.

How do I fix a Subject Alternative Name (SAN) mismatch?

A SAN mismatch means the hostname you are dialing is not listed in the broker certificate's Subject Alternative Names (or Common Name for older certificates). Certificate Manager shows all SANs after import. Your options are: dial a hostname that is covered by an existing SAN, reissue the broker certificate with the correct SANs, or use SNI override if your broker supports it. MQTT Commander's SNI override setting lets you send a different server name during TLS negotiation without changing the IP you connect to.

Can I share a diagnostic report without exposing my broker credentials?

Yes. Passwords, private keys, tokens, raw payloads, and private endpoint hostnames never reach the diagnostic log. The plain-language stage result still tells you what broke, so you can troubleshoot — or hand it to someone else — without exposing any credential material.

Get MQTT Commander

Native for iPhone & iPad. $2.99, one-time.

$2.99 · one-time purchase