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.

Coming soon to theApp Store See the features

Coming soon to the App Store · $2.99

Debug MQTT TLS and mTLS Connections on iOS
TLS + mTLS
Full mutual-TLS support with client certificate authentication
Chain & SAN
Validates certificate chain, expiry, CN, and Subject Alternative Names
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.” This eliminates the guesswork of correlating cryptic errSSL* codes against broker logs. The diagnostic bundle, with all secrets redacted, can be shared directly from the app for remote support without exposing credentials. Learn the full methodology in the TLS debugging guide.

Certificate Wizard: Import, Validate, and Configure in One Place

Certificate Wizard accepts .p12/.pfx bundles (with passphrase) and PEM files covering the CA certificate, client certificate, and private key. After import it immediately validates the chain, checks expiry, and confirms that the Subject Alternative Names or Common Name cover the hostname you are connecting to. SHA-256 fingerprints are displayed so you can cross-check against the broker. For mutual TLS the wizard 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 Wizard 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 Wizard, tap Add Certificate, choose CA Certificate, and import your PEM file. The wizard 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 Wizard 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 Wizard imports your client certificate and private key as a .p12/.pfx bundle or as separate PEM files, 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 Wizard 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. Connection Doctor's diagnostic bundle redacts all secrets—passwords, private keys, and tokens—before the report is assembled. You can share the bundle via AirDrop, Mail, or any iOS share sheet. Broker hostnames and certificate metadata are included so the recipient has enough context to help, but no credential material is present.

Get MQTT Commander

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

Coming soon to theApp Store

Coming soon to the App Store · $2.99