Introducción a cifrado SSL/TLS en MQTT y Flask

Certificados SSL, configuración HTTPS, comunicación segura

Módulo 6 ⏱️ 2 horas 🛠️ ESP32 + DHT11 🌐 Flask + MQTT

Introducción

La seguridad en las comunicaciones IoT es fundamental para proteger datos sensores y comandos de control. En esta clase aprenderemos a implementar cifrado SSL/TLS en nuestro stack ESP32 + DHT11 + Mosquitto + Flask, asegurando que las temperaturas y datos de humedad viajen de forma segura desde el sensor hasta nuestra aplicación web.

SSL/TLS (Secure Sockets Layer/Transport Layer Security) proporciona autenticación, integridad y confidencialidad en las comunicaciones. Para sistemas IoT profesionales, es esencial implementar estas medidas de seguridad desde el diseño inicial del proyecto.

Objetivo de la clase: Configurar comunicación segura completa desde ESP32 hasta Flask usando certificados SSL/TLS en MQTT y HTTPS.

Conceptos Fundamentales

SSL/TLS funciona mediante un protocolo de handshake que establece una conexión segura entre cliente y servidor. En nuestro contexto IoT, esto se aplica en dos niveles:

1. Arquitectura de Seguridad IoT

  • Nivel de Dispositivo: ESP32 → Broker MQTT (SSL/TLS)
  • Nivel de Aplicación: Cliente Web → Flask Server (HTTPS)
  • Broker Seguro: Mosquitto con certificados SSL
  • Autenticación: Certificados cliente y servidor

2. Componentes de Certificados SSL

  • CA (Certificate Authority): Autoridad que firma certificados
  • Certificado del Servidor: Identifica el broker MQTT o servidor Flask
  • Certificado del Cliente: Identifica dispositivos ESP32
  • Clave Privada: Clave secreta para descifrado
  • Clave Pública: Clave compartida para cifrado

3. Proceso de Handshake SSL/TLS

  1. Cliente envía "Client Hello" con versiones TLS soportadas
  2. Servidor responde "Server Hello" + certificado
  3. Cliente verifica certificado contra CA
  4. Intercambio de claves para sesión simétrica
  5. Activación de cifrado para datos de aplicación

4. Beneficios en Sistemas IoT

  • Confidencialidad: Datos de sensores cifrados
  • Integridad: Detección de modificaciones
  • Autenticación: Verificación de identidad de dispositivos
  • No repudio: Prueba de origen de datos

Implementación Práctica

Implementaremos SSL/TLS paso a paso, comenzando con la generación de certificados, configuración del broker Mosquitto, programación del ESP32 y finalmente el servidor Flask con HTTPS.

Paso 1: Generación de Certificados SSL


# Crear directorio para certificados
mkdir -p /etc/mosquitto/certs
cd /etc/mosquitto/certs

# Generar CA (Certificate Authority)
openssl req -new -x509 -days 3650 -extensions v3_ca -keyout ca.key -out ca.crt \
    -subj "/C=ES/ST=Madrid/L=Madrid/O=IoT-Company/OU=CA/CN=IoT-CA"

# Generar clave privada del servidor
openssl genrsa -out server.key 2048

# Crear solicitud de certificado del servidor
openssl req -new -key server.key -out server.csr \
    -subj "/C=ES/ST=Madrid/L=Madrid/O=IoT-Company/OU=Server/CN=localhost"

# Firmar certificado del servidor con CA
openssl x509 -req -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial \
    -out server.crt -days 365

# Generar certificado cliente para ESP32
openssl genrsa -out client.key 2048
openssl req -new -key client.key -out client.csr \
    -subj "/C=ES/ST=Madrid/L=Madrid/O=IoT-Company/OU=Device/CN=ESP32-DHT11"
openssl x509 -req -in client.csr -CA ca.crt -CAkey ca.key -CAcreateserial \
    -out client.crt -days 365
            

Paso 2: Configuración Mosquitto con SSL


# /etc/mosquitto/mosquitto.conf
port 1883
listener 8883

# Rutas de certificados
cafile /etc/mosquitto/certs/ca.crt
certfile /etc/mosquitto/certs/server.crt
keyfile /etc/mosquitto/certs/server.key

# Configuración SSL
tls_version tlsv1.2
require_certificate true
use_identity_as_username true

# Configuración de logging
log_dest file /var/log/mosquitto/mosquitto.log
log_type all

# Configuración de persistencia
persistence true
persistence_location /var/lib/mosquitto/

# Control de acceso
allow_anonymous false
            

Código ESP32 con SSL/TLS:


#include <WiFi.h>
#include <WiFiClientSecure.h>
#include <PubSubClient.h>
#include <DHT.h>
#include <ArduinoJson.h>

// Configuración DHT11
#define DHT_PIN 4
#define DHT_TYPE DHT11
DHT dht(DHT_PIN, DHT_TYPE);

// Configuración WiFi
const char* ssid = "Tu_WiFi";
const char* password = "Tu_Password";

// Configuración MQTT SSL
const char* mqtt_server = "192.168.1.100";
const int mqtt_port = 8883;
const char* mqtt_topic = "sensors/dht11";

// Certificados SSL (embebidos como strings)
const char* ca_cert = R"EOF(
-----BEGIN CERTIFICATE-----
MIIDXTCCAkWgAwIBAgIJAKoK/heBjcOuMA0GCSqGSIb3DQEBCwUAMEUxCzAJBgNV
BAYTAkVTMQ8wDQYDVQQIDAZNYWRyaWQxDzANBgNVBAcMBk1hZHJpZDEUMBIGA1UE
CgwLSW9ULUNvbXBhbnkwHhcNMjQwMTAxMDAwMDAwWhcNMzQwMTAxMDAwMDAwWjBF
MQswCQYDVQQGEwJFUzEPMA0GA1UECAwGTWFkcmlkMQ8wDQYDVQQHDAZNYWRyaWQx
FDASBgNVBAoMC0lvVC1Db21wYW55MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB
CgKCAQEA2K4mGzM8+X...
-----END CERTIFICATE-----
)EOF";

const char* client_cert = R"EOF(
-----BEGIN CERTIFICATE-----
MIIDXjCCAkagAwIBAgIJAKoK/heBjcOvMA0GCSqGSIb3DQEBCwUAMEUxCzAJBgNV
BAYTAkVTMQ8wDQYDVQQIDAZNYWRyaWQxDzANBgNVBAcMBk1hZHJpZDEUMBIGA1UE
CgwLSW9ULUNvbXBhbnkwHhcNMjQwMTAxMDAwMDAwWhcNMjUwMTAxMDAwMDAwWjBH
MQswCQYDVQQGEwJFUzEPMA0GA1UECAwGTWFkcmlkMQ8wDQYDVQQHDAZNYWRyaWQx
FjAUBgNVBAoMDUVTUDMyLURIVDExMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB
AQC7yK4mGzM8+X...
-----END CERTIFICATE-----
)EOF";

const char* client_key = R"EOF(
-----BEGIN PRIVATE KEY-----
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQC7yK4mGzM8+XtN
kYjKlOP9s2Bx7K4mGzM8+XtNkYjKlOP9s2Bx7K4mGzM8+XtNkYjKlOP9s2Bx...
-----END PRIVATE KEY-----
)EOF";

WiFiClientSecure espClient;
PubSubClient client(espClient);

void setup() {
    Serial.begin(115200);
    dht.begin();
    
    // Configurar certificados SSL
    espClient.setCACert(ca_cert);
    espClient.setCertificate(client_cert);
    espClient.setPrivateKey(client_key);
    
    // Conectar WiFi
    WiFi.begin(ssid, password);
    while (WiFi.status() != WL_CONNECTED) {
        delay(500);
        Serial.print(".");
    }
    Serial.println("WiFi conectado");
    Serial.print("IP: ");
    Serial.println(WiFi.localIP());
    
    // Configurar cliente MQTT
    client.setServer(mqtt_server, mqtt_port);
    client.setCallback(mqttCallback);
}

void loop() {
    if (!client.connected()) {
        reconnectMQTT();
    }
    client.loop();
    
    // Leer sensores y publicar cada 30 segundos
    static unsigned long lastRead = 0;
    if (millis() - lastRead > 30000) {
        publishSensorData();
        lastRead = millis();
    }
}

void reconnectMQTT() {
    while (!client.connected()) {
        Serial.print("Conectando MQTT SSL...");
        String clientId = "ESP32-DHT11-";
        clientId += String(random(0xffff), HEX);
        
        if (client.connect(clientId.c_str())) {
            Serial.println("conectado");
            client.subscribe("commands/esp32");
        } else {
            Serial.print("falló, rc=");
            Serial.print(client.state());
            Serial.println(" reintentando en 5 segundos");
            delay(5000);
        }
    }
}

void publishSensorData() {
    float temperature = dht.readTemperature();
    float humidity = dht.readHumidity();
    
    if (isnan(temperature) || isnan(humidity)) {
        Serial.println("Error leyendo DHT11");
        return;
    }
    
    // Crear JSON
    StaticJsonDocument<200> doc;
    doc["device"] = "ESP32-DHT11";
    doc["timestamp"] = millis();
    doc["temperature"] = temperature;
    doc["humidity"] = humidity;
    doc["location"] = "Lab-IoT";
    
    String payload;
    serializeJson(doc, payload);
    
    // Publicar con SSL
    if (client.publish(mqtt_topic, payload.c_str())) {
        Serial.println("Datos publicados: " + payload);
    } else {
        Serial.println("Error publicando datos");
    }
}

void mqttCallback(char* topic, byte* message, unsigned int length) {
    String messageTemp;
    for (int i = 0; i < length; i++) {
        messageTemp += (char)message[i];
    }
    Serial.println("Mensaje recibido: " + messageTemp);
}
            

Cliente MQTT SSL en Python:


import ssl
import json
import paho.mqtt.client as mqtt
from datetime import datetime
import logging

# Configuración logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

class SecureMQTTClient:
    def __init__(self, broker_host, broker_port=8883):
        self.broker_host = broker_host
        self.broker_port = broker_port
        self.client = mqtt.Client()
        self.client.on_connect = self.on_connect
        self.client.on_message = self.on_message
        self.client.on_disconnect = self.on_disconnect
        self.sensor_data = {}
        
    def setup_ssl(self, ca_cert_path, client_cert_path, client_key_path):
        """Configurar certificados SSL/TLS"""
        context = ssl.create_default_context(ssl.Purpose.SERVER_AUTH)
        context.check_hostname = False
        context.verify_mode = ssl.CERT_REQUIRED
        
        # Cargar certificados
        context.load_verify_locations(ca_cert_path)
        context.load_cert_chain(client_cert_path, client_key_path)
        
        self.client.tls_set_context(context)
        logger.info("Certificados SSL configurados")
        
    def connect(self):
        """Conectar al broker MQTT con SSL"""
        try:
            self.client.connect(self.broker_host, self.broker_port, 60)
            self.client.loop_start()
            logger