Para testar uma conexão MQTT com TLS, verifique três camadas em ordem: o próprio handshake TLS com openssl s_client, depois uma sessão MQTT real com mosquitto_sub ou mosquitto_pub e, por fim, a biblioteca cliente que os seus dispositivos usam. Cada camada pode falhar por motivos diferentes, e testá-las separadamente mostra exatamente qual está quebrada.
Todos os exemplos usam broker.connectmq.com na porta 8883 (MQTT sobre TLS). Troque o host pelo do seu broker.
Passo 1: verifique o handshake com openssl
O openssl s_client fala TLS, mas não MQTT — exatamente o que você quer: ele mostra o certificado que o broker envia e se a sua máquina confia nele, sem nenhuma biblioteca MQTT no caminho.
openssl s_client -connect broker.connectmq.com:8883 \
-servername broker.connectmq.com \
-verify_hostname broker.connectmq.com \
-showcerts </dev/nullO que cada opção faz:
-servernameenvia o SNI (Server Name Indication). Brokers e load balancers que hospedam vários nomes usam isso para escolher o certificado. Sem ele, você pode receber um certificado padrão e um erro de hostname.-verify_hostnamefaz o openssl verificar se o certificado é válido para esse nome — a mesma verificação que o seu cliente MQTT faz.-showcertsimprime todos os certificados enviados pelo servidor, para você ver se o intermediário está incluído.</dev/nullfecha a conexão logo após o handshake, em vez de esperar entrada.
No fim da saída, procure:
Verify return code: 0 (ok)Qualquer outra coisa é um problema que você também verá no seu cliente MQTT:
| Resultado do openssl | Significado |
|---|---|
Verify return code: 20 (unable to get local issuer certificate) | A CA (ou um intermediário ausente) não está no seu trust store |
Verify return code: 18 (self-signed certificate) | O certificado do servidor é autoassinado; passe a CA com -CAfile |
Verify return code: 19 (self-signed certificate in certificate chain) | A cadeia termina em uma raiz privada em que você não confia |
Verify return code: 10 (certificate has expired) | Renove o certificado no broker |
Verify return code: 62 (hostname mismatch) | O certificado não lista o nome ao qual você se conectou |
wrong version number | Essa porta é MQTT sem criptografia, não TLS (geralmente 1883) |
connect: Connection refused / timeout | Porta fechada ou filtrada — não é problema de TLS |
Para um broker com CA privada, adicione -CAfile ca.crt e rode o comando de novo. Se ele retornar 0 (ok), seus certificados estão corretos e o cliente só precisa do mesmo arquivo de CA.
Inspecione o certificado rapidamente
openssl s_client -connect broker.connectmq.com:8883 \
-servername broker.connectmq.com </dev/null 2>/dev/null \
| openssl x509 -noout -subject -issuer -dates -ext subjectAltNameConfira se subjectAltName contém exatamente o hostname que os seus dispositivos usam. Clientes modernos ignoram o Common Name do certificado, então um nome que aparece só no CN não conta. Para uma verificação de CI que falhe duas semanas antes do vencimento, passe o certificado para openssl x509 -noout -checkend 1209600.
Para testar quais versões de TLS o broker aceita, repita o primeiro comando com -tls1_2 ou -tls1_3. Um dispositivo com uma pilha TLS antiga que só fala TLS 1.2 precisa que o broker mantenha o 1.2 ativado — veja TLS 1.2 vs 1.3 no MQTT.
Passo 2: rode uma sessão MQTT real sobre TLS
Um handshake bem-sucedido não prova que o listener MQTT por trás funciona. Assine e publique pela 8883:
mosquitto_sub -h broker.connectmq.com -p 8883 \
--cafile /etc/ssl/certs/ca-certificates.crt \
-u "$MQTT_USER" -P "$MQTT_PASS" \
-t testmqtt/tls/check -v -dmosquitto_pub -h broker.connectmq.com -p 8883 \
--cafile /etc/ssl/certs/ca-certificates.crt \
-u "$MQTT_USER" -P "$MQTT_PASS" \
-t testmqtt/tls/check -m "hello over tls"Os clientes mosquitto só ativam TLS quando você informa uma opção de CA. Use --cafile com o seu bundle de CAs (o caminho acima é do Debian/Ubuntu; no macOS com Homebrew fica em $(brew --prefix)/etc/ca-certificates/cert.pem), --capath /etc/ssl/certs ou --tls-use-os-certs no mosquitto 2.x. Para uma CA privada, aponte --cafile para o seu ca.crt. Para TLS mútuo, adicione --cert client.crt --key client.key — veja certificados de cliente e mTLS.
Quando falha, o mosquitto é lacônico: imprime Error: A TLS error occurred. e nada mais. Volte ao passo 1 — o openssl diz por quê. Se o openssl funciona mas o mosquitto falha, compare o hostname: -h precisa ser o nome do certificado, não um endereço IP.
Passo 3: teste a partir do código do cliente
Por fim, teste com a biblioteca que a sua aplicação usa. O repositório de certificados e os padrões mudam de linguagem para linguagem, então uma conexão que funciona no mosquitto ainda pode falhar aqui.
import os
import paho.mqtt.client as mqtt
def on_connect(client, userdata, flags, reason_code, properties):
print("CONNACK:", reason_code)
client.publish("testmqtt/tls/check", "hello from python over tls", qos=1)
c = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2, protocol=mqtt.MQTTv5)
c.tls_set() # CAs do sistema, verificação de hostname ativa; ca_certs="ca.crt" para uma CA privada
c.username_pw_set(os.environ["MQTT_USER"], os.environ["MQTT_PASS"])
c.on_connect = on_connect
c.connect("broker.connectmq.com", 8883)
c.loop_forever()import mqtt from "mqtt";
const client = mqtt.connect("mqtts://broker.connectmq.com:8883", {
username: process.env.MQTT_USER,
password: process.env.MQTT_PASS,
// ca: fs.readFileSync("ca.crt"), // só para uma CA privada
reconnectPeriod: 0,
});
client.on("connect", () => {
console.log("connected over TLS");
client.publish("testmqtt/tls/check", "hello from node over tls", () => client.end());
});
client.on("error", (err) => console.error(err.code, err.message));Os erros que eles produzem são justamente os mais pesquisados:
| Biblioteca | Erro | Causa comum |
|---|---|---|
| Python | [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unable to get local issuer certificate | CA ou intermediário ausente |
| Python | certificate verify failed: Hostname mismatch, certificate is not valid for '...' | Conexão por IP ou nome errado |
| Python | [SSL: WRONG_VERSION_NUMBER] wrong version number | Cliente TLS em uma porta sem criptografia |
| Node.js | UNABLE_TO_VERIFY_LEAF_SIGNATURE — unable to verify the first certificate | O servidor não envia o intermediário |
| Node.js | SELF_SIGNED_CERT_IN_CHAIN | CA privada não informada em ca |
| Node.js | ERR_TLS_CERT_ALTNAME_INVALID — Hostname/IP does not match certificate's altnames | Nome fora da lista SAN |
As correções estão detalhadas uma a uma em certificate verify failed, falha no handshake TLS e hostname mismatch. O gerador de código cria trechos de conexão com TLS para outras linguagens.
Checklist rápido de testes TLS
openssl s_clientcom-servernamee-verify_hostnameretornaVerify return code: 0 (ok).-showcertslista o certificado do servidor e o intermediário.- O SAN contém exatamente o hostname usado pelos clientes; o certificado não está perto de vencer.
mosquitto_subemosquitto_pubtrocam uma mensagem na8883com a verificação ativada.- A biblioteca da sua aplicação conecta com o trust store padrão (ou com a CA privada que você distribui).
- A porta sem criptografia
1883está fechada ou desativada em produção se TLS for obrigatório. - Dispositivos com pilhas TLS antigas conectam com a versão de TLS e os cipher suites que você permite.
Quer também uma verificação pelo navegador? O cliente MQTT online conecta via WebSockets seguros na 8084, que em muitas configurações usa o mesmo certificado — um jeito rápido de confirmar o lado TLS do broker antes de depurar um dispositivo.
Testar TLS camada por camada transforma um vago "erro de TLS" em uma causa específica em poucos minutos. Mantenha o comando do openssl e a verificação de validade nos seus scripts e execute-os sempre que um certificado for renovado ou um load balancer mudar.
Perguntas frequentes
Como verifico se um broker MQTT suporta TLS?
Rode openssl s_client -connect host:8883 -servername host. Se o handshake funcionar, você verá a cadeia de certificados e 'Verify return code: 0 (ok)'. Um erro 'wrong version number' indica que a porta fala MQTT sem criptografia, não TLS.
Por que o TLS funciona no navegador mas falha no meu dispositivo?
Normalmente o servidor não envia o certificado intermediário. Navegadores conseguem buscar intermediários ausentes; openssl, mosquitto e pilhas TLS embarcadas não. Confira se s_client -showcerts lista o intermediário e corrija o arquivo de certificado do broker.
É seguro usar --insecure ou tls_insecure_set(True)?
Só como diagnóstico temporário na sua própria máquina. Essas opções desativam a verificação de hostname, o que deixa a conexão aberta a ataques man-in-the-middle. Corrija o certificado ou o hostname e mantenha a verificação ativada.
