14.4.5. Einen öffentlichen Server verifizieren (Kamera als Client)¶
Alles auf der vorherigen Seite darüber, dass ein Client „die Root bereits besitzt“, trifft auf Browser, Telefone und PCs zu – auf die Kamera trifft es nicht zu. Das ssl von MicroPython wird ohne eingebauten Trust-Store ausgeliefert: Eine frisch geflashte Kamera vertraut überhaupt keiner CA, und die Voreinstellung (ssl.CERT_NONE) verifiziert nichts und ist einem Man-in-the-Middle weit geöffnet. Wenn die Kamera also der Client ist, der sich nach außen zu einem öffentlichen TLS-Server verbindet (eine HTTPS-API, ein MQTT-Broker, …), und Sie möchten, dass sie diesen Server wirklich verifiziert, müssen Sie den Vertrauensanker selbst bereitstellen.
Die Mechanik ist dieselbe wie beim Beispiel mit selbstsigniertem Client unter Selbstsignierte Zertifikate; der einzige Unterschied besteht darin, dass die Datei, die Sie laden, ein echtes CA-Zertifikat ist statt des eigenen Zertifikats der Gegenstelle:
Beschaffen Sie das CA-Zertifikat, das die Kette des Servers verankert. „Verankert“ bedeutet das Zertifikat an (oder nahe) der Spitze der Serverkette, das Sie als Ausgangspunkt Ihres Vertrauens wählen. Ein TLS-Server sendet sein Leaf und üblicherweise seine Intermediate(s); seine Root sendet er niemals. Sie müssen diesen Vertrauensanker selbst und unabhängig vom Server beschaffen – einfach allem zu vertrauen, was ein Server Ihnen überreicht, würde den gesamten Sinn der Verifizierung zunichtemachen.
Finden Sie zunächst heraus, welche CA das Zertifikat des Servers tatsächlich ausgestellt hat. Zum Beispiel gegen
openmv.io:openssl s_client -connect openmv.io:443 -showcerts < /dev/nullDer
Certificate chain-Block listet jedes Zertifikat mit seinem Subject (s:) und Issuer (i:) auf; neuere OpenSSL-Versionen geben außerdema:-Zeilen (Schlüsseltyp) undv:-Zeilen (Gültigkeit) aus, die Sie hier ignorieren können:Certificate chain 0 s:CN=openmv.io i:C=US, O=Let's Encrypt, CN=E8 1 s:C=US, O=Let's Encrypt, CN=E8 i:C=US, O=Internet Security Research Group, CN=ISRG Root X1
Eintrag 0 ist das Leaf (
openmv.io), ausgestellt durch das IntermediateE8. Eintrag 1 ist dieses Intermediate, ausgestellt durch die RootISRG Root X1. Der Issuer (i:) des obersten Eintrags nennt die Root – hierISRG Root X1. (Das Intermediate istE8statt desR10/R11, das Sie anderswo gesehen haben mögen, weilopenmv.ioein ECDSA-Zertifikat verwendet; Let’s Encrypt signiert ECDSA-Leaves mit seinenE-Reihen-Intermediates und RSA-Leaves mit seinenR-Reihen-Intermediates. Beide verketten sich zuISRG Root X1.)OpenSSL gibt außerdem
depth=-Zeilen aus und meldet die Root möglicherweise mitVerification: OK. Das geschieht nur, weil Ihr PCISRG Root X1bereits vertraut – der Server hat sie nicht gesendet (ein Server sendet seine Root niemals), und die Kamera, die keinen Trust-Store hat, wird sie ebenfalls nicht besitzen. Genau deshalb müssen Sie sie bereitstellen.Laden Sie diese Root von den eigenen veröffentlichten Roots der CA herunter. Let’s Encrypt katalogisiert alle ihre Roots auf der Let’s Encrypt certificates page; die direkte Datei für ISRG Root X1 ist isrgrootx1.pem (sie bieten sie auch vorkodiert als isrgrootx1.der an). Andere CAs veröffentlichen ihre Roots auf einer ähnlichen „root certificates“- / „repository“-Seite; die kanonische öffentliche Sammlung ist das Mozilla CA program (CCADB). Vergewissern Sie sich, dass Sie die richtige Datei abgerufen haben, indem Sie ihren Fingerprint mit dem von der CA veröffentlichten Wert vergleichen (fügen Sie
-inform DERhinzu, falls Sie die.derheruntergeladen haben):openssl x509 -in isrgrootx1.pem -noout -subject -fingerprint -sha256Falls Sie lieber keine Root nachverfolgen möchten, können Sie stattdessen das Intermediate direkt aus der
-showcerts-Ausgabe kopieren (der zweite-----BEGIN CERTIFICATE------Block), diesem vertrauen und akzeptieren, dass Sie es jedes Mal aktualisieren müssen, wenn die CA das Intermediate rotiert – weit häufiger als die Root (siehe den Kompromiss weiter unten).Konvertieren Sie es nach DER, genau wie zuvor:
openssl x509 -in isrgrootx1.pem -outform DER -out ca.derKopieren Sie
ca.derauf die Kamera (Dateisystem oder ROMFS) und laden Sie es als Vertrauensanker:import socket import ssl import ntptime ntptime.settime() # validity check needs the clock addr = socket.getaddrinfo("api.example.com", 443)[0][-1] sock = socket.socket() sock.connect(addr) ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT) ctx.verify_mode = ssl.CERT_REQUIRED ctx.load_verify_locations(cafile="ca.der") ssock = ctx.wrap_socket(sock, server_hostname="api.example.com")
server_hostnameist hier erforderlich: Es steuert SNI und ist der Name, der gegen densubjectAltNamedes Serverzertifikats geprüft wird.
Tipp
Abkürzung für den Normalfall. Let’s Encrypt ist die am weitesten verbreitete öffentliche CA, und sowohl ihre RSA- als auch ihre ECDSA-Zertifikate verketten sich derzeit zu ISRG Root X1 (wie das obige openmv.io-Beispiel zeigt). Falls die Server, mit denen Ihre Kamera spricht, Let’s Encrypt verwenden, können Sie die Inspektion vollständig überspringen: Legen Sie einfach isrgrootx1.der auf die Kamera und führen Sie load_verify_locations damit aus.
Dies bringt TLS nicht zu jeder Site zum Funktionieren. Ein Server, dessen Zertifikat von einer anderen CA stammt (DigiCert, Google Trust Services, Amazon, Sectigo, …), wird die Verifizierung weiterhin nicht bestehen, und da die Kamera pro ssl.SSLContext einem einzigen DER-Zertifikat vertraut, können Sie nicht jede Root bündeln, wie es ein Browser tut. Im Zweifelsfall ermitteln Sie die tatsächliche CA des Servers wie oben gezeigt und vertrauen dieser Root.
Welchem Zertifikat Sie vertrauen, ist ein Kompromiss:
Die Root (empfohlen). Langlebig – oft jahrzehntelang – sodass sich
ca.derselten ändert. Es erfordert, dass der Server sein Intermediate sendet, damit mbedTLS den Pfad Leaf → Intermediate → Ihre vertrauenswürdige Root aufbauen kann; praktisch jeder korrekt konfigurierte öffentliche Server tut dies.Das Intermediate. Funktioniert ebenfalls und funktioniert weiterhin, selbst wenn ein Server das Intermediate auslässt, aber Intermediates werden weit häufiger rotiert als Roots, sodass Sie
ca.derhäufiger aktualisieren müssen.Das Leaf selbst (Certificate Pinning). Am strengsten, aber das Leaf ändert sich bei jeder Erneuerung – bei Let’s Encrypt etwa alle 90 Tage – sodass dies nur dann sinnvoll ist, wenn Sie auch den Server kontrollieren und den neuen Pin im Gleichschritt auf jede Kamera ausrollen können. Genau das tut das Beispiel mit selbstsigniertem Client.
Bemerkung
ssl.SSLContext.load_verify_locations() nimmt ein einzelnes DER-kodiertes CA-Zertifikat entgegen, sodass die Kamera jeweils genau einem Anker vertraut. Um Server unter verschiedenen CAs zu erreichen, verwenden Sie pro Anker einen separaten ssl.SSLContext. Und da dieses Zertifikat selbst irgendwann abläuft oder von der CA rotiert wird, behandeln Sie es wie jedes andere Zertifikat auf dem Gerät.