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.