14.4.3. Zelfondertekende certificaten#
Een zelfondertekend certificaat is de snelste manier om TLS werkend te krijgen tussen twee apparaten die je beheert: beide uiteinden vertrouwen één enkel certificaat dat je zelf genereert. Het dekt elke uitrol waarbij je beide zijden van de verbinding configureert – een openbare Certificate Authority komt pas in beeld wanneer externe clients moeten verbinden zonder te worden verteld een aangepast certificaat te vertrouwen.
14.4.3.1. Een zelfondertekend certificaat aanmaken#
Voer OpenSSL uit op je ontwikkelmachine. De subjectAltName (SAN) is wat moderne TLS-clients controleren tijdens hostnaamverificatie, dus stel die in op de hostnaam(en) en/of het IP-adres(sen) die clients zullen gebruiken om de camera te bereiken (CN alleen is verouderd en wordt door veel clients genegeerd). Vervang DNS:openmv / IP:192.168.1.50 door het adres waarmee je clients daadwerkelijk verbinden.
ECDSA P-256 – aanbevolen:
# 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 – sterker, groter/langzamer:
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 – maximale compatibiliteit:
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"
Notitie
Een client-certificaat (gebruikt voor wederzijdse authenticatie, hieronder) wordt aangemaakt met exact dezelfde opdrachten – er is niets clientspecifieks aan het certificaat zelf. Genereer gewoon een tweede, onafhankelijk sleutel/certificaat-paar onder andere namen (bijv. client.key / client.crt) en gebruik het op de client zoals getoond in het mTLS-voorbeeld. De subjectAltName is alleen van belang voor de zijde wiens hostnaam de peer verifieert (de client controleert de naam van de server; niets controleert die van de client), dus kan worden weggelaten voor een uitsluitend client-certificaat. De -subj / CN is eveneens slechts een label op een clientcertificaat – de serverzijde controleert hier alleen of het certificaat naar een vertrouwde CA herleidt, hij matcht de naam nooit – dus stel die in op wat die client ook maar identificeert (bijv. /CN=sensor-01). Houd hoe dan ook een -subj-waarde aan, zodat OpenSSL het certificaat niet-interactief kan genereren.
De levensduur van het certificaat wordt ingesteld met -days; certificaten verlopen en moeten daarvoor opnieuw worden gegenereerd en uitgerold.
14.4.3.2. Converteren naar DER#
Converteer zowel het certificaat als de privésleutel naar DER voordat je ze naar de camera kopieert:
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. Bestanden naar de camera kopiëren#
Kopieer de DER-bestanden naar het bestandssysteem van de camera – bijvoorbeeld door ze naar de USB-schijf van de OpenMV Cam te slepen, of met mpremote cp server.der : en mpremote cp server.key.der :. Kopieer aan de verifiërende zijde ook het CA-/peer-certificaat in DER-vorm.
De DER-bestanden hoeven niet op het beschrijfbare bestandssysteem te staan. MicroPython kan ook een alleen-lezen ROMFS-image aankoppelen op /rom, en daar geplaatste certificaten worden precies zo geladen als elk ander bestand – bijv. ctx.load_cert_chain("/rom/server.der", "/rom/server.key.der"). Een ROMFS-image wordt voorbereid op je ontwikkelmachine en is alleen-lezen tijdens runtime, dus het certificaat kan niet op het apparaat worden gewijzigd – handig om een productie-eenheid te vergrendelen. Let op dat een in ROMFS opgeslagen privésleutel nog steeds leesbaar is voor code die op de camera draait; ROMFS beschermt tegen wijziging, niet tegen uitlezing. Een in ROMFS aanwezig certificaat kan alleen worden vervangen door het image opnieuw te bouwen en te flashen.
14.4.3.4. Het certificaat gebruiken#
Een volledige client die de klok instelt, een socket opent, een zelfondertekende server verifieert en gegevens uitwisselt:
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()
Een volledige server die zijn certificaat en sleutel aanbiedt:
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()
Voor wederzijdse authenticatie (mTLS) vereist en verifieert de server bovendien een clientcertificaat, en biedt de client er een van zichzelf aan:
# 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")
Zie de documentatie van de ssl-module voor de volledige API.
Notitie
Alles op deze pagina is ongewijzigd van toepassing op DTLS (TLS over UDP). De sleutels, certificaten, het DER-formaat, het vertrouwensmodel, de verloopkwesties en de load_cert_chain- / load_verify_locations-aanroepen zijn identiek; alleen het transport verschilt – je omhult een socket.SOCK_DGRAM-socket en selecteert ssl.PROTOCOL_DTLS_CLIENT / ssl.PROTOCOL_DTLS_SERVER in plaats van de TLS-protocolconstanten. De enige extra complicatie is een anti-spoofing-cookie aan de serverzijde – van de eerste verbinding van een nieuwe client wordt verwacht dat die mislukt en de client probeert het simpelweg opnieuw; zie DTLS-ondersteuning voor details.