14.4.3. Selbstsignierte Zertifikate¶
Ein selbstsigniertes Zertifikat ist der schnellste Weg, TLS zwischen zwei von Ihnen kontrollierten Geräten zum Laufen zu bringen: Beide Enden vertrauen einem einzelnen Zertifikat, das Sie selbst erzeugen. Es deckt jede Bereitstellung ab, bei der Sie beide Seiten der Verbindung konfigurieren – eine öffentliche Zertifizierungsstelle kommt erst ins Spiel, wenn sich Drittanbieter-Clients verbinden müssen, ohne angewiesen zu werden, einem benutzerdefinierten Zertifikat zu vertrauen.
14.4.3.1. Ein selbstsigniertes Zertifikat erstellen¶
Führen Sie OpenSSL auf Ihrem Entwicklungsrechner aus. Der subjectAltName (SAN) ist das, was moderne TLS-Clients bei der Hostnamen-Verifizierung prüfen, setzen Sie ihn also auf die Hostnamen und/oder IP-Adressen, über die Clients die Kamera erreichen (CN allein ist veraltet und wird von vielen Clients ignoriert). Ersetzen Sie DNS:openmv / IP:192.168.1.50 durch die Adresse, mit der sich Ihre Clients tatsächlich verbinden.
ECDSA P-256 – empfohlen:
# 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 – stärker, größer/langsamer:
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 Kompatibilität:
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"
Bemerkung
Ein Client-Zertifikat (verwendet für die gegenseitige Authentifizierung, siehe unten) wird mit genau denselben Befehlen erstellt – am Zertifikat selbst ist nichts Client-spezifisch. Erzeugen Sie einfach ein zweites, unabhängiges Schlüssel-/Zertifikatspaar unter anderen Namen (z. B. client.key / client.crt) und verwenden Sie es auf dem Client wie im mTLS-Beispiel gezeigt. Der subjectAltName ist nur für die Seite relevant, deren Hostnamen der Peer verifiziert (der Client prüft den Namen des Servers; den Client prüft niemand), daher kann er bei einem reinen Client-Zertifikat weggelassen werden. Das -subj / CN ist bei einem Client-Zertifikat ebenfalls nur eine Bezeichnung – die Serverseite prüft hier nur, dass das Zertifikat zu einer vertrauenswürdigen CA verkettet ist, sie gleicht den Namen nie ab – setzen Sie es also auf das, was diesen Client identifiziert (z. B. /CN=sensor-01). Behalten Sie in jedem Fall einen -subj-Wert bei, damit OpenSSL das Zertifikat nicht-interaktiv erzeugen kann.
Die Zertifikatslebensdauer wird über -days festgelegt; Zertifikate laufen ab und müssen vorher neu erzeugt und erneut bereitgestellt werden.
14.4.3.2. Nach DER konvertieren¶
Konvertieren Sie sowohl das Zertifikat als auch den privaten Schlüssel nach DER, bevor Sie sie auf die Kamera kopieren:
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. Dateien auf die Kamera kopieren¶
Kopieren Sie die DER-Dateien in das Dateisystem der Kamera – zum Beispiel, indem Sie sie auf das USB-Laufwerk der OpenMV Cam ziehen, oder mit mpremote cp server.der : und mpremote cp server.key.der :. Kopieren Sie auf der verifizierenden Seite zusätzlich das CA-/Peer-Zertifikat in DER-Form.
Die DER-Dateien müssen nicht im beschreibbaren Dateisystem liegen. MicroPython kann auch ein schreibgeschütztes ROMFS-Image unter /rom einhängen, und dort abgelegte Zertifikate werden genau wie jede andere Datei geladen – z. B. ctx.load_cert_chain("/rom/server.der", "/rom/server.key.der"). Ein ROMFS-Image wird auf Ihrem Entwicklungsrechner vorbereitet und ist zur Laufzeit schreibgeschützt, sodass das Zertifikat auf dem Gerät nicht verändert werden kann – nützlich, um eine Produktionseinheit abzuriegeln. Beachten Sie, dass ein in ROMFS gespeicherter privater Schlüssel weiterhin von auf der Kamera laufendem Code lesbar ist; ROMFS schützt vor Änderung, nicht vor Extraktion. Ein in ROMFS residentes Zertifikat lässt sich nur durch Neubau und erneutes Flashen des Images ersetzen.
14.4.3.4. Das Zertifikat verwenden¶
Ein vollständiger Client, der die Uhr stellt, einen Socket öffnet, einen selbstsignierten Server verifiziert und Daten austauscht:
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()
Ein vollständiger Server, der sein Zertifikat und seinen Schlüssel vorlegt:
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()
Für die gegenseitige Authentifizierung (mTLS) verlangt und verifiziert der Server zusätzlich ein Client-Zertifikat, und der Client legt ein eigenes vor:
# 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")
Die vollständige API finden Sie in der Dokumentation des Moduls ssl.
Bemerkung
Alles auf dieser Seite gilt unverändert für DTLS (TLS über UDP). Die Schlüssel, Zertifikate, das DER-Format, das Vertrauensmodell, die Ablaufproblematik sowie die Aufrufe load_cert_chain / load_verify_locations sind identisch; nur der Transport unterscheidet sich – Sie umhüllen einen socket.SOCK_DGRAM-Socket und wählen ssl.PROTOCOL_DTLS_CLIENT / ssl.PROTOCOL_DTLS_SERVER anstelle der TLS-Protokollkonstanten. Die eine zusätzliche Feinheit ist ein serverseitiger Anti-Spoofing-Cookie – die erste Verbindung eines neuen Clients schlägt erwartungsgemäß fehl, und der Client versucht es einfach erneut; Einzelheiten siehe DTLS-Unterstützung.