Cuando aparece un error en el handshake TLS de MQTT, la conexión muere antes incluso de que empiece MQTT, y la mayoría de las librerías cliente lo notifican con un mensaje poco útil. Esta guía te enseña a aislar la causa en minutos con openssl s_client y después repasa los seis problemas que hay detrás de casi todos los fallos: puerto erróneo, CA ausente, SNI, desfase de reloj, versión de TLS incompatible y proxies que interceptan el tráfico. Todas las pruebas se hacen contra broker.connectmq.com en el puerto 8883, así que puedes comparar un endpoint que funciona con el tuyo.
Qué ocurre en un handshake TLS
Antes de que tu cliente envíe el paquete CONNECT de MQTT, debe completar un handshake TLS con el broker:
- El cliente envía un ClientHello con las versiones de TLS admitidas, los cipher suites y el nombre del servidor (SNI).
- El broker responde con la versión y el cifrado elegidos, además de su cadena de certificados.
- El cliente verifica la cadena contra las raíces de confianza, comprueba las fechas de validez y compara el hostname.
- Ambos lados derivan las claves y pasan a tráfico cifrado.
Un fallo en cualquier paso cierra la conexión. Tu librería MQTT muestra entonces algo como A TLS error occurred (mosquitto), ssl.SSLError (Python), ECONNRESET o un error de certificado (Node.js), o state=-2 (PubSubClient en ESP32). Lo que necesitas es la causa subyacente, y OpenSSL la muestra directamente. Para la configuración de referencia correcta, consulta nuestra guía de configuración TLS en MQTT.
Paso 1: reproduce el handshake con openssl s_client
Ejecuta esto desde la misma máquina o red que el cliente que falla:
openssl s_client -connect broker.connectmq.com:8883 \
-servername broker.connectmq.com -showcerts </dev/null
Un resultado correcto termina con líneas parecidas a:
Verify return code: 0 (ok)
y muestra el protocolo y el cifrado negociados. Si OpenSSL funciona pero tu aplicación falla, el problema está en la configuración de la aplicación (ruta de la CA, hostname, opciones de la librería). Si OpenSSL también falla, el problema es la red, el almacén de confianza o el reloj. Esa sola comparación reduce el espacio de búsqueda a la mitad.
Variantes útiles:
# Resumen breve del protocolo, el cifrado y la verificación
openssl s_client -connect broker.connectmq.com:8883 -servername broker.connectmq.com -brief </dev/null
# Verificar contra un archivo de CA concreto, como haría un dispositivo embebido
openssl s_client -connect broker.connectmq.com:8883 -servername broker.connectmq.com \
-CAfile isrgrootx1.pem </dev/null
# Inspeccionar las fechas y los nombres del certificado
openssl s_client -connect broker.connectmq.com:8883 -servername broker.connectmq.com </dev/null 2>/dev/null \
| openssl x509 -noout -subject -issuer -dates -ext subjectAltName
Error en el handshake TLS de MQTT: síntomas y causas
| Síntoma | Causa probable | Solución |
|---|---|---|
wrong version number o packet length too long |
Cliente TLS hablando con un puerto en claro (1883) | Usa el puerto 8883 para TLS |
| La conexión se queda colgada y acaba agotando el tiempo | Cliente MQTT en claro en el puerto 8883, o puerto bloqueado por un firewall | Activa TLS en el cliente; comprueba la accesibilidad |
unable to get local issuer certificate (código 20) |
El cliente no confía en la CA raíz | Instala o apunta al paquete de CA correcto |
self-signed certificate in certificate chain (código 19) |
Un proxy o antivirus intercepta y sustituye el certificado | Evita la inspección o confía deliberadamente en la CA del proxy |
certificate is not yet valid (código 9) |
El reloj del dispositivo va atrasado | Sincroniza la hora con NTP antes de conectar |
certificate has expired (código 10) |
El reloj va adelantado o el certificado del servidor ha caducado de verdad | Revisa el reloj del dispositivo y las fechas del servidor |
Hostname mismatch / IP address mismatch |
Conexión por IP o con un nombre incorrecto | Conéctate con el hostname que figura en el certificado |
no protocols available / unsupported protocol |
Cliente limitado a versiones de TLS que el servidor no ofrece | Permite TLS 1.2 o superior en el cliente |
alert handshake failure |
Ningún cipher suite común o falta de SNI | Actualiza la librería TLS; envía SNI |
Causa 1: puerto o esquema erróneos
Es el error más común. El puerto 1883 habla MQTT en claro; el 8883 espera TLS desde el primer byte. Si tu cliente inicia TLS contra el 1883, OpenSSL indica wrong version number porque la respuesta del servidor no es un registro TLS. Si un cliente en claro se conecta al 8883, el broker espera un ClientHello que nunca llega y la conexión acaba cerrándose.
Revisa también el esquema de la URL. En MQTT.js, mqtt:// significa en claro y mqtts:// significa TLS; en navegadores, usa wss://broker.connectmq.com:8084/mqtt. En Python, lo que activa TLS es llamar a tls_set(); cambiar solo el puerto no basta.
Para comprobar si el puerto es accesible:
# Linux/macOS
nc -vz broker.connectmq.com 8883
# Windows PowerShell
Test-NetConnection broker.connectmq.com -Port 8883
Las redes corporativas y de invitados suelen bloquear el 8883. Si está bloqueado, WebSocket seguro en el 8084 es una alternativa habitual; consulta MQTT sobre WebSocket.
Causa 2: certificado de CA ausente o incorrecto
El error de verificación 20 significa que el cliente no pudo construir una cadena hasta una raíz de confianza. Situaciones típicas:
- Los clientes de mosquitto no usan el almacén del sistema salvo que pases
--capath /etc/ssl/certs(Linux) o--cafile. En Windows, descarga el PEM de ISRG Root X1 y pásalo con--cafile. - Las imágenes Docker mínimas a menudo no incluyen el paquete
ca-certificates. - Los dispositivos embebidos recibieron la raíz equivocada, un PEM truncado o el certificado hoja en lugar de la raíz.
- Python en macOS instalado desde python.org necesita ejecutar una vez su instalador de certificados; los detalles están en SSL CERTIFICATE_VERIFY_FAILED en MQTT.
El certificado del broker de ConnectMQ lo emite Let's Encrypt, así que cualquier almacén de confianza del sistema actualizado ya incluye la raíz necesaria.
Causa 3: falta de SNI
Server Name Indication le dice al servidor qué hostname quieres. Los endpoints con balanceo de carga y multi-tenant pueden presentar un certificado por defecto, o rechazar el handshake, cuando falta SNI. La mayoría de las librerías envían SNI automáticamente cuando te conectas con un hostname. Los problemas aparecen cuando el código resuelve primero la dirección IP y se conecta a la IP, o cuando se usan pilas TLS muy antiguas. Pasa siempre el hostname a la librería cliente y, en las pruebas con OpenSSL, incluye siempre -servername.
Causa 4: desfase de reloj
Los certificados solo son válidos entre dos fechas. Una Raspberry Pi sin hora de red, un ESP32 que no ha terminado NTP o una máquina virtual restaurada desde una instantánea antigua pueden ir días o décadas desfasados. Compruébalo con date -u en Linux y, en los microcontroladores, espera a NTP antes de abrir la conexión TLS. La guía de certificate pinning en ESP32 incluye un bucle de espera de NTP listo para usar.
Causa 5: versión de TLS o cifrado incompatibles
Un firmware antiguo, runtimes de Java heredados o ajustes fijados en el código como ssl.PROTOCOL_TLSv1 de Python pueden ofrecer solo versiones que los servidores modernos han desactivado. Comprueba qué versiones acepta un servidor:
openssl s_client -connect broker.connectmq.com:8883 -servername broker.connectmq.com -tls1_2 </dev/null
openssl s_client -connect broker.connectmq.com:8883 -servername broker.connectmq.com -tls1_3 </dev/null
Configura tu cliente para que negocie automáticamente (por ejemplo, ssl.PROTOCOL_TLS_CLIENT en Python) en lugar de fijar una sola versión. Nuestro artículo sobre MQTT con TLS 1.3 explica con más detalle la elección de versiones y cifrados.
Causa 6: inspección TLS y proxies
Algunos firewalls corporativos y antivirus interceptan TLS presentando su propio certificado. El síntoma es el error de verificación 19 y un emisor con el nombre del producto de seguridad. Pide a tu equipo de red que excluya el hostname del broker o, si la inspección es obligatoria, confía explícitamente en la CA de la organización solo en ese entorno. Nunca distribuyas dispositivos con la verificación desactivada para esquivar un proxy.
Checklist sistemática
- Ejecuta
openssl s_clientcon-servernamedesde el entorno que falla. - Confirma que el puerto coincide con el transporte: 1883 en claro, 8883 TLS, 8084 WebSocket seguro.
- Confirma que el cliente se conecta por hostname, no por IP.
- Comprueba que el almacén de confianza o el archivo de CA contiene la raíz correcta.
- Revisa el reloj del sistema en UTC.
- Permite la negociación automática de la versión de TLS.
- Descarta proxies probando desde otra red, como el punto de acceso del teléfono.
- Solo cuando el handshake funcione, depura los errores a nivel MQTT, como credenciales incorrectas o un conflicto de client ID.
El último punto es importante: un handshake correcto seguido de una desconexión con un reason code en el CONNACK es un problema de autenticación o de protocolo, no de TLS.
Menos problemas de handshake con un broker MQTT gestionado
Muchos incidentes TLS en brokers autoalojados vienen del lado del servidor: certificados caducados, cadenas incompletas o un listener mal configurado tras una actualización. Un broker MQTT gestionado traslada esa responsabilidad al proveedor. ConnectMQ sirve TLS en el 8883 y WebSocket seguro en el 8084 con certificados de Let's Encrypt renovados automáticamente, así que si un handshake falla, sabes que debes mirar en el lado del cliente. Puedes probar cualquier cliente primero contra el servidor MQTT público gratuito (solo para pruebas, no para datos de producción), y nuestra guía de broker MQTT en la nube explica cómo encajan los brokers alojados en producción.
Empieza con ConnectMQ
Crea un broker gratuito en app.connectmq.com con el plan Serverless, sin tarjeta de crédito, copia las credenciales generadas y usa el cliente MQTT en el navegador del portal para confirmar que el broker funciona antes de depurar el código de tu propio dispositivo.
Preguntas frecuentes
¿Por qué mosquitto_sub solo dice "A TLS error occurred"?
Los clientes de mosquitto ocultan el detalle de OpenSSL. Ejecuta openssl s_client con el mismo host, puerto y archivo de CA para ver el error de verificación exacto.
¿Puede un usuario o una contraseña incorrectos provocar un fallo del handshake TLS?
No. Las credenciales se comprueban después del handshake, dentro del intercambio CONNECT de MQTT. Un problema de credenciales aparece como un rechazo en el CONNACK, no como un error TLS.
Mi cliente funciona en la Wi-Fi de casa pero no en la oficina. ¿Por qué?
Probablemente la red de la oficina bloquea el puerto 8883 o inspecciona el tráfico TLS. Pruébalo con Test-NetConnection o nc e intenta usar WebSocket seguro en el puerto 8084.
¿Debo desactivar la verificación de certificados para conseguir conectarme?
No. Desactivar la verificación oculta el problema real y elimina la protección frente a brokers impostores. Corrige en su lugar la CA, el hostname o el reloj.



