Módulo 9

Uso seguro de MQTT con TLS/SSL

Seguridad y Protocolos Avanzados

ESP32 Mecatrónica IoT UNAM

Introducción Teórica

Fundamentos de MQTT y Seguridad TLS/SSL en Comunicaciones IoT

¿Qué es MQTT?

MQTT (Message Queuing Telemetry Transport) es un protocolo de mensajería ligero diseñado específicamente para dispositivos con recursos limitados y conexiones de red de baja velocidad. En el contexto de la mecatrónica y el Internet de las Cosas (IoT), MQTT se ha convertido en el estándar de facto para la comunicación entre dispositivos y servidores.

Características Clave de MQTT:
  • Protocolo publish-subscribe: Desacopla emisores y receptores
  • Ligero: Mínimo overhead de red (2 bytes para headers)
  • Calidad de Servicio (QoS): 3 niveles de garantía de entrega
  • Persistencia: Mensajes retenidos y sesiones limpias
  • Last Will Testament: Notificación automática de desconexión
Arquitectura MQTT
MQTT Broker
Mosquitto, HiveMQ
Publisher
ESP32 Sensors
Subscriber
Dashboard

Seguridad: TLS/SSL en MQTT

TLS/SSL Fundamentals

TLS (Transport Layer Security) y su predecesora SSL (Secure Sockets Layer) son protocolos criptográficos que proporcionan comunicaciones seguras sobre redes no confiables. En MQTT, TLS/SSL garantiza:

  • Confidencialidad: Cifrado de datos en tránsito
  • Integridad: Verificación de que los datos no han sido alterados
  • Autenticación: Verificación de identidad del servidor y cliente
Amenazas sin TLS/SSL
Espionaje: Interceptación de datos sensibles
Manipulación: Alteración de comandos de control
Suplantación: Dispositivos fraudulentos
Caso de Uso Industrial
Sistema de Automatización de Fábrica

En una fábrica automatizada moderna, cientos de sensores y actuadores se comunican constantemente mediante MQTT:

  • Sensores de temperatura: Monitoreo de hornos industriales
  • Sensores de presión: Sistemas hidráulicos y neumáticos
  • Actuadores de válvulas: Control de flujo de materiales
  • Motores de producción: Control de velocidad y posición
Riesgos sin TLS/SSL:
  • 🔥 Sobrecalentamiento no detectado
  • 💥 Sobrepresurización de sistemas
  • ⚠️ Parada no autorizada de producción
  • 💸 Pérdidas económicas millonarias
Solución TLS/SSL: El cifrado TLS previene estos ataques garantizando que solo dispositivos autorizados puedan enviar comandos críticos y que todos los datos de sensores lleguen íntegros y confidenciales al sistema de control central.

Explicación Técnica Detallada

Implementación de MQTT con TLS/SSL en ESP32 usando Arduino IDE

ESP32 y Comunicación Segura

El ESP32 es un microcontrolador de bajo costo con capacidades integradas de Wi-Fi y Bluetooth, convirtiéndolo en la opción ideal para aplicaciones IoT industriales. Su arquitectura dual-core permite manejar simultáneamente la comunicación segura TLS/SSL y el procesamiento de datos de sensores.

Requisitos para MQTT con TLS/SSL:
📚 Librerías Necesarias:
  • WiFi.h - Conectividad Wi-Fi
  • WiFiClientSecure.h - TLS/SSL
  • PubSubClient.h - Cliente MQTT
  • ArduinoJson.h - Manejo JSON
🔐 Certificados Requeridos:
  • CA Certificate: Autoridad certificadora
  • Client Certificate: Identidad del ESP32
  • Private Key: Clave privada del cliente
Especificaciones ESP32
CPU: Dual-core 240MHz
RAM: 520KB SRAM
Wi-Fi: 802.11 b/g/n
TLS: v1.2 Hardware
Cifrado: AES, RSA, SHA

Implementación Completa

Cliente MQTT Seguro para ESP32
#include <WiFi.h>
#include <WiFiClientSecure.h>
#include <PubSubClient.h>
#include <ArduinoJson.h>

// ==================== CONFIGURACIÓN DE RED ====================
const char* ssid = "TU_WIFI_SSID";
const char* password = "TU_WIFI_PASSWORD";

// ==================== CONFIGURACIÓN MQTT SEGURO ====================
const char* mqtt_server = "mqtt.industrialiot.com";  
const int mqtt_port = 8883;  // Puerto seguro MQTT sobre TLS
const char* mqtt_username = "esp32_client";
const char* mqtt_password = "secure_password_2024";
const char* client_id = "ESP32_Factory_001";

// ==================== CERTIFICADOS TLS ====================
const char* ca_cert = \
"-----BEGIN CERTIFICATE-----\n" \
"MIIDQTCCAimgAwIBAgITBmyfz5m/jAo54vB4ikPmljZbyjANBgkqhkiG9w0BAQsF\n" \
"ADA5MQswCQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6\n" \
"... [certificado completo aquí] ...\n" \
"-----END CERTIFICATE-----\n";

// ==================== OBJETOS PRINCIPALES ====================
WiFiClientSecure espClient;
PubSubClient client(espClient);

// ==================== VARIABLES DE SENSORES ====================
float temperature = 0.0;
float humidity = 0.0;
unsigned long lastSensorRead = 0;
const unsigned long sensorInterval = 30000; // 30 segundos

// ==================== CONFIGURACIÓN INICIAL ====================
void setup() {
    Serial.begin(115200);
    Serial.println("🚀 ESP32 MQTT TLS/SSL Client v2.0");
    
    // Inicializar pines de sensores
    pinMode(2, OUTPUT); // LED indicador
    
    // Conectar a WiFi
    setupWiFi();
    
    // Configurar cliente MQTT seguro
    setupMQTTSecure();
    
    Serial.println("✅ Sistema iniciado correctamente");
}

// ==================== CONFIGURACIÓN WIFI ====================
void setupWiFi() {
    Serial.print("🌐 Conectando a WiFi: ");
    Serial.println(ssid);
    
    WiFi.begin(ssid, password);
    
    int attempts = 0;
    while (WiFi.status() != WL_CONNECTED && attempts < 20) {
        delay(1000);
        Serial.print(".");
        attempts++;
    }
    
    if (WiFi.status() == WL_CONNECTED) {
        Serial.println("\n✅ WiFi conectado!");
        Serial.print("📍 IP address: ");
        Serial.println(WiFi.localIP());
        Serial.print("📡 Signal strength: ");
        Serial.println(WiFi.RSSI());
    } else {
        Serial.println("\n❌ Error: No se pudo conectar a WiFi");
        ESP.restart();
    }
}

// ==================== CONFIGURACIÓN MQTT SEGURO ====================
void setupMQTTSecure() {
    Serial.println("🔐 Configurando conexión MQTT segura...");
    
    // Configurar certificado CA para validar servidor
    espClient.setCACert(ca_cert);
    
    // Opcional: Configurar certificado de cliente para autenticación mutua
    // espClient.setCertificate(client_cert);
    // espClient.setPrivateKey(client_key);
    
    // Configurar servidor MQTT
    client.setServer(mqtt_server, mqtt_port);
    client.setCallback(messageCallback);
    
    // Buffer más grande para mensajes JSON
    client.setBufferSize(1024);
    
    Serial.println("✅ Configuración MQTT completada");
}

// ==================== CALLBACK PARA MENSAJES RECIBIDOS ====================
void messageCallback(char* topic, byte* payload, unsigned int length) {
    Serial.print("📨 Mensaje recibido en tópico: ");
    Serial.println(topic);
    
    // Convertir payload a string
    String message = "";
    for (int i = 0; i < length; i++) {
        message += (char)payload[i];
    }
    
    Serial.print("💬 Mensaje: ");
    Serial.println(message);
    
    // Procesar comandos remotos
    processRemoteCommand(topic, message);
}

// ==================== PROCESAMIENTO DE COMANDOS REMOTOS ====================
void processRemoteCommand(String topic, String message) {
    if (topic.endsWith("/commands")) {
        JsonDocument doc;
        deserializeJson(doc, message);
        
        String command = doc["cmd"];
        if (command == "restart") {
            Serial.println("🔄 Comando de reinicio recibido");
            delay(1000);
            ESP.restart();
        }
        else if (command == "led_on") {
            digitalWrite(2, HIGH);
            Serial.println("💡 LED encendido por comando remoto");
        }
        else if (command == "led_off") {
            digitalWrite(2, LOW);
            Serial.println("💡 LED apagado por comando remoto");
        }
    }
}

// ==================== BUCLE PRINCIPAL ====================
void loop() {
    // Mantener conexión MQTT
    if (!client.connected()) {
        reconnectMQTT();
    }
    client.loop();
    
    // Leer sensores cada 30 segundos
    if (millis() - lastSensorRead > sensorInterval) {
        readSensors();
        publishSensorData();
        lastSensorRead = millis();
    }
    
    delay(100); // Pequeño delay para estabilidad
}

// ==================== RECONEXIÓN MQTT ====================
void reconnectMQTT() {
    Serial.println("🔄 Intentando reconexión MQTT...");
    
    while (!client.connected()) {
        Serial.print("🔌 Conectando a broker MQTT: ");
        Serial.println(mqtt_server);
        
        if (client.connect(client_id, mqtt_username, mqtt_password)) {
            Serial.println("✅ Conectado al broker MQTT!");
            
            // Suscribirse a tópicos de comandos
            String commandTopic = String("factory/esp32/") + client_id + "/commands";
            client.subscribe(commandTopic.c_str());
            Serial.println("📥 Suscrito a: " + commandTopic);
            
            // Publicar mensaje de conectividad
            publishConnectionStatus(true);
            
        } else {
            Serial.print("❌ Error de conexión MQTT, rc=");
            Serial.println(client.state());
            Serial.println("🔄 Reintentando en 5 segundos...");
            delay(5000);
        }
    }
}

// ==================== LECTURA DE SENSORES ====================
void readSensors() {
    // Simular lectura de sensores (reemplazar con código real)
    temperature = 22.5 + random(-50, 50) / 10.0; // Simular variación
    humidity = 65.0 + random(-100, 100) / 10.0;
    
    // Parpadear LED para indicar lectura
    digitalWrite(2, HIGH);
    delay(100);
    digitalWrite(2, LOW);
}

// ==================== PUBLICACIÓN DE DATOS ====================
void publishSensorData() {
    JsonDocument doc;
    
    // Crear payload JSON con datos del sensor
    doc["device_id"] = client_id;
    doc["timestamp"] = millis();
    doc["temperature"] = temperature;
    doc["humidity"] = humidity;
    doc["wifi_rssi"] = WiFi.RSSI();
    doc["free_heap"] = ESP.getFreeHeap();
    doc["uptime"] = millis() / 1000;
    
    String jsonString;
    serializeJson(doc, jsonString);
    
    // Publicar en múltiples tópicos
    String dataTopic = String("factory/sensors/") + client_id + "/data";
    
    if (client.publish(dataTopic.c_str(), jsonString.c_str(), true)) { // retained=true
        Serial.println("📤 Datos publicados: " + jsonString);
    } else {
        Serial.println("❌ Error al publicar datos");
    }
}

// ==================== PUBLICAR ESTADO DE CONEXIÓN ====================
void publishConnectionStatus(bool connected) {
    JsonDocument doc;
    doc["device_id"] = client_id;
    doc["connected"] = connected;
    doc["timestamp"] = millis();
    doc["ip_address"] = WiFi.localIP().toString();
    
    String jsonString;
    serializeJson(doc, jsonString);
    
    String statusTopic = String("factory/status/") + client_id;
    client.publish(statusTopic.c_str(), jsonString.c_str(), true);
    
    Serial.println(connected ? "🟢 Estado: Conectado" : "🔴 Estado: Desconectado");
}
Análisis del Código
🔐 Características de Seguridad:
  • Certificado CA: Validación del servidor
  • Puerto 8883: MQTT sobre TLS/SSL
  • Autenticación: Usuario y contraseña
  • Mensajes retenidos: Persistencia de estado
⚡ Optimizaciones:
  • Reconexión automática: Tolerancia a fallas
  • Buffer expandido: Mensajes JSON grandes
  • Watchdog: Reinicio automático
  • Telemetría: Monitoreo de sistema

Ejercicios Prácticos Visuales

Implementaciones paso a paso de MQTT seguro en diferentes escenarios

Publisher MQTT Seguro - DHT22

Básico 30-45 min TLS 1.2

Objetivo: Desarrollar un sensor IoT que publique datos de temperatura y humedad en un broker MQTT seguro usando cifrado TLS/SSL.

Materiales Requeridos:
  • ESP32 DevKit V1
  • Sensor DHT22 (±0.5°C precisión)
  • Resistor pull-up 4.7kΩ
  • Protoboard y cables jumper
  • Fuente 5V/1A
Especificaciones Técnicas:
  • Protocolo: MQTT 3.1.1 sobre TLS 1.2
  • Frecuencia: Lecturas cada 10 segundos
  • QoS: Nivel 1 (At least once)
  • Payload: JSON con timestamp
  • Retained: Último valor persistente
Resultados Esperados:

Datos cifrados de temperatura y humedad publicados cada 10s en tópico sensors/dht22/{device_id}/data con validación de integridad TLS.

Subscriber MQTT + Control Remoto

Intermedio 45-60 min Auth 2FA

Objetivo: Implementar un sistema de control remoto seguro que reciba comandos cifrados vía MQTT y controle actuadores industriales.

Componentes Electrónicos:
  • ESP32 con módulo TLS hardware
  • Relé 5V/10A (control cargas AC)
  • LED RGB indicador de estado
  • Buzzer piezo (alertas sonoras)
  • Optoacoplador 4N35 (aislación)
Características de Seguridad:
  • Autenticación mutua: Client + Server certificates
  • Command validation: Hash SHA-256
  • Timeout protection: Auto-desconexión 30s
  • Fail-safe: Estado seguro en caso de falla

Sistema Multi-Sensor Industrial

Avanzado 60-90 min HA Cluster

Objetivo: Desarrollar una estación de monitoreo industrial completa con múltiples sensores, actuadores y comunicación MQTT segura tolerante a fallos.

Sensores Integrados:
  • 📡 DHT22 - Temp/Humedad ambiente
  • 🌊 Ultrasonido - Nivel de tanques
  • ⚡ ACS712 - Corriente eléctrica
  • 💨 MQ-135 - Calidad del aire
  • 📈 MPU6050 - Vibración/Inclinación
Control de Actuadores:
  • 🔵 Válvulas solenoides (4x)
  • 🟢 Motores paso a paso (2x)
  • 🔴 Alarms & Warning lights
  • 📢 Sirenas industriales
  • 🖥️ Display información local
Arquitectura de Tópicos:
factory/line1/station_{id}/
  • sensors/temperature - Datos térmicos
  • sensors/vibration - FFT Analysis
  • actuators/valves - Control hidráulico
  • alerts/critical - Emergencias
  • status/heartbeat - Keep-alive

Proyecto Aplicado

Sistema Integral de Monitoreo Industrial con MQTT Seguro

Sistema de Monitoreo de Planta Industrial

Implementación completa de una solución IoT industrial que integra múltiples sensores críticos y actuadores de control, comunicándose a través de MQTT con cifrado TLS/SSL end-to-end. El sistema proporciona monitoreo en tiempo real, alertas automáticas y control remoto seguro para aplicaciones de manufactura crítica.

Características Destacadas
  • 🔒 Seguridad Enterprise: TLS 1.3 con autenticación mutua
  • 📊 Analytics en Tiempo Real: InfluxDB + Grafana
  • 🚨 Sistema de Alertas: SMS/Email/Slack integration
  • ⚡ Alta Disponibilidad: Cluster MQTT redundante
  • 📱 Dashboard Responsive: Control desde cualquier dispositivo
  • 🔄 OTA Updates: Actualización firmware remota
  • 💾 Data Persistence: Buffer local + Cloud backup
  • 🔧 Mantenimiento Predictivo: ML para detección anomalías
Métricas del Sistema
50+ Sensores Conectados
99.9% Uptime Garantizado
<100ms Latencia Promedio
256-bit Cifrado AES

Integración Sensores/Actuadores

Sensores Críticos
Temperatura (±0.1°C) DS18B20 industrial
Humedad (±2% RH) SHT30 calibrado
Presión (±0.5 bar) Transductor 4-20mA
Corriente AC/DC Pinza amperimétrica
Control de Actuadores
Válvulas Solenoides Control ON/OFF 24V
Motores Servo Posición angular precisa
Luces de Alarma Torre lumínica RGB
Sirena Industrial Alertas sonoras 110dB
Lista de Materiales
Core Components:
  • ESP32 WROOM-32D (x3) - $45
  • Raspberry Pi 4 (Broker) - $85
  • Power Supply 24V/5A - $35
Sensores Industriales:
  • DS18B20 (x8) - $40
  • Pressure transmitter - $120
  • Current clamp - $65
💰 Total Estimado: $750 USD

Roadmap de Implementación

1
Fase de Diseño e Infraestructura

Configuración del broker MQTT seguro, generación de certificados TLS, diseño de la topología de red industrial y definición de tópicos MQTT.

⏱️ 1-2 días
2
Desarrollo del Firmware ESP32

Implementación del cliente MQTT seguro, integración de drivers de sensores, sistema de logging local y protocolo de reconexión automática.

⏱️ 3-5 días
3
Integración y Testing

Pruebas de carga del sistema MQTT, validación de seguridad TLS, testing de tolerancia a fallos y benchmarking de performance.

⏱️ 2-3 días
4
Despliegue en Producción

Instalación en ambiente industrial, configuración de monitoring y alertas, documentación técnica y capacitación del personal.

⏱️ 1-2 días

Evaluación y Troubleshooting

Diagnóstico avanzado, solución de problemas y criterios de evaluación profesional

Problemas Comunes y Soluciones Expertas

Error de Conexión MQTT Broker
CRÍTICO

Síntomas: client.connect() retorna false, código de error -2 o -4

🔧 Diagnóstico paso a paso:
  1. Verificar conectividad básica:
    ping mqtt.broker.com
  2. Validar puerto TLS:
    telnet mqtt.broker.com 8883
  3. Debug certificado CA:
    espClient.setInsecure(); // Solo para testing
  4. Verificar credenciales: Usuario/contraseña correctos
  5. Revisar Client ID: Debe ser único por conexión
Fallo en Validación de Certificados TLS/SSL
ALTO

Síntomas: "SSL certificate verify failed", handshake_failure

🔍 Verificaciones:
  • Certificado CA válido y actualizado
  • Formato PEM correcto (\n incluidos)
  • Fecha de expiración del certificado
  • CN del certificado coincide con hostname
🛠️ Herramientas de Debug:
// Habilitar debug SSL
espClient.setDebugMsgTypes(SSL_INFO);

// Verificar memoria heap
Serial.println(ESP.getFreeHeap());

// Test certificado online
openssl s_client -connect broker:8883
Agotamiento de Memoria Heap
MEDIO

Síntomas: Crashes aleatorios, guru meditation error, reconnect loops

⚡ Optimizaciones de Memoria:
Buffer MQTT:
client.setBufferSize(512); // Reducir si es necesario
Certificados en Flash:
const char* ca_cert PROGMEM = "...";
JSON Stream:
// Usar streaming en lugar de buffer completo
Watchdog Feed:
yield(); // En loops largos
Criterios de Evaluación
🔒 Seguridad (40%)
15% Implementación correcta TLS/SSL
10% Validación de certificados
10% Autenticación robusta
5% Manejo seguro de credenciales
⚡ Funcionalidad (35%)
20% Pub/Sub funcionando correctamente
10% Reconexión automática
5% QoS y retained messages
🔧 Código (25%)
15% Estructura y comentarios
10% Manejo de errores
📊 Cálculo de Calificación:
Nota = (Seg×0.4) + (Func×0.35) + (Code×0.25)
✅ Mínimo para aprobar: 70%