14.4.3. Certificados autofirmados¶
Un certificado autofirmado es la forma más rápida de hacer funcionar TLS entre dos dispositivos que usted controla: ambos extremos confían en un único certificado que usted mismo genera. Cubre todos los despliegues en los que configura ambos lados de la conexión; una Autoridad de Certificación pública entra en juego solo cuando clientes de terceros tienen que conectarse sin que se les indique que confíen en un certificado personalizado.
14.4.3.1. Creación de un certificado autofirmado¶
Ejecute OpenSSL en su máquina de desarrollo. El subjectAltName (SAN) es lo que comprueban los clientes TLS modernos durante la verificación del nombre de host, así que configúrelo con el nombre o nombres de host o las direcciones IP que los clientes usarán para alcanzar la cámara (CN por sí solo es heredado y muchos clientes lo ignoran). Reemplace DNS:openmv / IP:192.168.1.50 por la dirección a la que sus clientes se conectan realmente.
ECDSA P-256 – recomendado:
# Generate a P-256 private key.
openssl ecparam -name prime256v1 -genkey -noout -out server.key
# Self-signed certificate valid for one year, with a SAN.
openssl req -new -x509 -key server.key -out server.crt -days 365 \
-subj "/CN=openmv" -addext "subjectAltName=DNS:openmv,IP:192.168.1.50"
ECDSA P-384 – más robusto, más grande/lento:
openssl ecparam -name secp384r1 -genkey -noout -out server.key
openssl req -new -x509 -key server.key -out server.crt -days 365 \
-subj "/CN=openmv" -addext "subjectAltName=DNS:openmv,IP:192.168.1.50"
RSA-2048 – máxima compatibilidad:
openssl req -new -x509 -newkey rsa:2048 -nodes -keyout server.key \
-out server.crt -days 365 -subj "/CN=openmv" \
-addext "subjectAltName=DNS:openmv,IP:192.168.1.50"
Nota
Un certificado de cliente (usado para la autenticación mutua, más abajo) se crea con exactamente estos mismos comandos; no hay nada específico de cliente en el certificado en sí. Simplemente genere un segundo par independiente de clave/certificado con nombres distintos (p. ej. client.key / client.crt) y úselo en el cliente como se muestra en el ejemplo de mTLS. El subjectAltName solo importa para el lado cuyo nombre de host verifica el par (el cliente comprueba el nombre del servidor; nada comprueba el del cliente), por lo que puede omitirse en un certificado solo de cliente. El -subj / CN es asimismo solo una etiqueta en un certificado de cliente –el lado del servidor aquí solo comprueba que el certificado encadena hasta una CA de confianza, nunca hace coincidir el nombre–, así que configúrelo con lo que identifique a ese cliente (p. ej. /CN=sensor-01). Mantenga algún valor de -subj en cualquier caso, para que OpenSSL pueda generar el certificado de forma no interactiva.
El periodo de vida del certificado se establece con -days; los certificados caducan y deben regenerarse y volver a desplegarse antes de entonces.
14.4.3.2. Conversión a DER¶
Convierta tanto el certificado como la clave privada a DER antes de copiarlos a la cámara:
openssl x509 -in server.crt -outform DER -out server.der
openssl pkey -in server.key -outform DER -out server.key.der
14.4.3.3. Copia de archivos a la cámara¶
Copie los archivos DER al sistema de archivos de la cámara –por ejemplo arrastrándolos a la unidad USB de la OpenMV Cam, o con mpremote cp server.der : y mpremote cp server.key.der :. En el lado verificador, copie también el certificado de la CA / del par en formato DER.
Los archivos DER no tienen por qué residir en el sistema de archivos con permisos de escritura. MicroPython también puede montar una imagen ROMFS de solo lectura en /rom, y los certificados colocados allí se cargan exactamente igual que cualquier otro archivo, p. ej. ctx.load_cert_chain("/rom/server.der", "/rom/server.key.der"). Una imagen ROMFS se prepara en su máquina de desarrollo y es de solo lectura en tiempo de ejecución, por lo que el certificado no puede alterarse en el dispositivo, lo que resulta útil para blindar una unidad de producción. Tenga en cuenta que una clave privada almacenada en ROMFS sigue siendo legible por el código que se ejecuta en la cámara; ROMFS protege contra la modificación, no contra la extracción. Un certificado residente en ROMFS solo puede reemplazarse reconstruyendo y reflasheando la imagen.
14.4.3.4. Uso del certificado¶
Un cliente completo que configura el reloj, abre un socket, verifica un servidor autofirmado e intercambia datos:
import socket
import ssl
import ntptime
ntptime.settime() # correct clock for the validity check
# Open a plain TCP connection.
addr = socket.getaddrinfo("openmv", 8443)[0][-1]
sock = socket.socket()
sock.connect(addr)
# Wrap it for TLS, trusting the server's self-signed certificate.
ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
ctx.verify_mode = ssl.CERT_REQUIRED
ctx.load_verify_locations(cafile="server.der")
ssock = ctx.wrap_socket(sock, server_hostname="openmv")
ssock.write(b"hello\n")
print(ssock.read())
ssock.close()
Un servidor completo que presenta su certificado y su clave:
import socket
import ssl
import ntptime
ntptime.settime() # correct clock for the validity check
ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
ctx.load_cert_chain("server.der", "server.key.der")
sock = socket.socket()
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock.bind(socket.getaddrinfo("0.0.0.0", 8443)[0][-1])
sock.listen(1)
while True:
client, addr = sock.accept()
sclient = ctx.wrap_socket(client, server_side=True)
sclient.write(b"hello\n")
print(sclient.read())
sclient.close()
Para la autenticación mutua (mTLS) el servidor además requiere y verifica un certificado de cliente, y el cliente presenta uno propio:
# Server side: also demand and verify a client certificate.
ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
ctx.load_cert_chain("server.der", "server.key.der")
ctx.verify_mode = ssl.CERT_REQUIRED
ctx.load_verify_locations(cafile="client.der")
# Client side: present a certificate of our own.
ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
ctx.load_cert_chain("client.der", "client.key.der")
ctx.verify_mode = ssl.CERT_REQUIRED
ctx.load_verify_locations(cafile="server.der")
Consulte la documentación del módulo ssl para la API completa.
Nota
Todo lo de esta página se aplica sin cambios a DTLS (TLS sobre UDP). Las claves, los certificados, el formato DER, el modelo de confianza, las cuestiones de caducidad y las llamadas a load_cert_chain / load_verify_locations son idénticas; solo difiere el transporte: envuelve un socket socket.SOCK_DGRAM y selecciona ssl.PROTOCOL_DTLS_CLIENT / ssl.PROTOCOL_DTLS_SERVER en lugar de las constantes de protocolo TLS. La única complicación adicional es una cookie anti-suplantación del lado del servidor: se espera que la primera conexión de un cliente nuevo falle y el cliente simplemente reintenta; consulte Compatibilidad con DTLS para más detalles.