ssl — SSL/TLS-Modul

Dieses Modul bietet Zugriff auf die Verschlüsselungs- und Peer-Authentifizierungsfunktionen von Transport Layer Security (früher und weithin bekannt als „Secure Sockets Layer“) für Netzwerk-Sockets, sowohl auf der Client- als auch auf der Serverseite.

Tipp

Neu bei TLS auf der Kamera? Beginnen Sie mit dem Tutorial Arbeiten mit TLS-Zertifikaten. Es erklärt Schritt für Schritt die Auswahl von Schlüsseltypen, das Erstellen und Konvertieren von Zertifikaten in das von der Kamera benötigte DER-Format, das Übertragen auf das Gerät sowie das Verifizieren von Servern und Clients – mit vollständigen, lauffähigen Beispielen.

Bemerkung

MicroPython implementiert ssl.SSLError nicht. SSL/TLS-Fehler werden stattdessen als OSError ausgelöst.

Beispiele

TLS-Client, der das Zertifikat des Servers gegen ein im Dateisystem gespeichertes CA-Zertifikat (im DER-Format) verifiziert:

import socket
import ssl
import ntptime

# CERT_REQUIRED checks the certificate's validity dates, so the clock
# must be set (see the certificates tutorial linked above).
ntptime.settime()

# Open a plain TCP connection.
addr = socket.getaddrinfo("example.com", 443)[0][-1]
sock = socket.socket()
sock.connect(addr)

# Wrap it for TLS and require a valid certificate.
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="example.com")

ssock.write(b"GET / HTTP/1.0\r\nHost: example.com\r\n\r\n")
print(ssock.read())
ssock.close()

Für eine schnelle, unsichere Verbindung (ohne Zertifikatsvalidierung) kann stattdessen die Komfortfunktion ssl.wrap_socket() verwendet werden:

ssock = ssl.wrap_socket(sock, server_hostname="example.com")

TLS-Server, der sein eigenes Zertifikat und seinen privaten Schlüssel (DER-Format) präsentiert:

import socket
import ssl

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)

ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
ctx.load_cert_chain("server.der", "server.key")

while True:
    client, addr = sock.accept()
    sclient = ctx.wrap_socket(client, server_side=True)
    sclient.write(b"hello\n")
    sclient.close()

Funktionen

ssl.wrap_socket(sock: Any, server_side: bool = False, key: bytes | None = None, cert: bytes | None = None, cert_reqs: int = CERT_NONE, cadata: bytes | None = None, server_hostname: str | None = None, do_handshake: bool = True) Any

Umhüllt das angegebene sock und gibt ein neues umhülltes Socket-Objekt zurück. Diese Funktion erstellt zunächst einen SSLContext und ruft dann die Methode SSLContext.wrap_socket() auf diesem Kontextobjekt auf. Die Argumente sock, server_side und server_hostname werden unverändert an den Methodenaufruf weitergereicht. Das Argument do_handshake wird als do_handshake_on_connect weitergereicht. Die übrigen Argumente verhalten sich wie folgt:

  • cert_reqs legt fest, ob der Peer (Server oder Client) ein gültiges Zertifikat präsentieren muss. Beachten Sie, dass ssl.CERT_NONE und ssl.CERT_OPTIONAL kein Zertifikat validieren; nur ssl.CERT_REQUIRED tut dies.

  • cadata ist ein bytes-Objekt, das die CA-Zertifikatskette (im DER-Format) enthält, die das Zertifikat des Peers validiert. Derzeit wird nur ein einzelnes DER-codiertes Zertifikat unterstützt.

Klassen

class ssl.SSLContext(protocol: int, /)

Erstellt eine neue SSLContext-Instanz. Das Argument protocol muss eine der PROTOCOL_*-Konstanten sein.

load_cert_chain(certfile: str | bytes, keyfile: str | bytes) None

Lädt einen privaten Schlüssel und das zugehörige Zertifikat. certfile ist eine Zeichenkette mit dem Dateipfad des Zertifikats. keyfile ist eine Zeichenkette mit dem Dateipfad des privaten Schlüssels.

Unterschied zu CPython

MicroPython-Erweiterung: certfile und keyfile können statt Zeichenketten auch bytes-Objekte sein; in diesem Fall werden sie als die eigentlichen Zertifikats-/Schlüsseldaten interpretiert.

load_verify_locations(cafile: str | None = None, cadata: bytes | None = None) None

Lädt die CA-Zertifikatskette, die das Zertifikat des Peers validiert. cafile ist der Dateipfad der CA-Zertifikate. cadata ist ein bytes-Objekt, das die CA-Zertifikate enthält. Es sollte nur eines dieser Argumente angegeben werden.

get_ciphers() List[str]

Gibt eine Liste der aktivierten Cipher zurück, als Liste von Zeichenketten.

set_ciphers(ciphers: List[str]) None

Legt die verfügbaren Cipher für Sockets fest, die mit diesem Kontext erstellt werden. ciphers sollte eine Liste von Zeichenketten im IANA-Cipher-Suite-Format sein.

wrap_socket(sock: Any, *, server_side: bool = False, do_handshake_on_connect: bool = True, server_hostname: str | None = None, client_id: bytes | None = None) Any

Nimmt einen stream sock (üblicherweise eine socket.socket-Instanz vom Typ SOCK_STREAM) und gibt eine Instanz von ssl.SSLSocket zurück, die den zugrunde liegenden Stream umhüllt. Das zurückgegebene Objekt verfügt über die üblichen Methoden der stream-Schnittstelle wie read(), write() usw.

  • server_side wählt aus, ob sich der umhüllte Socket auf der Server- oder Clientseite befindet. Ein serverseitiger SSL-Socket sollte aus einem normalen Socket erstellt werden, der von accept() auf einem nicht-SSL-Listening-Server-Socket zurückgegeben wird.

  • do_handshake_on_connect legt fest, ob der Handshake als Teil von wrap_socket durchgeführt wird oder ob er auf die ersten Lese- bzw. Schreibvorgänge verschoben wird. Bei blockierenden Sockets ist es üblich, den Handshake sofort durchzuführen. Bei nicht-blockierenden Sockets (d. h. wenn das an wrap_socket übergebene sock im nicht-blockierenden Modus ist) sollte der Handshake im Allgemeinen verschoben werden, da wrap_socket sonst blockiert, bis er abgeschlossen ist.

  • server_hostname ist für die Verwendung als Client gedacht und legt den Hostnamen fest, der gegen das empfangene Serverzertifikat geprüft wird. Außerdem legt es den Namen für Server Name Indication (SNI) fest, sodass der Server das passende Zertifikat präsentieren kann.

  • client_id ist ein MicroPython-spezifisches Erweiterungsargument, das nur bei der Implementierung eines DTLS-Servers verwendet wird. Details siehe DTLS-Unterstützung.

Warnung

Standardmäßig wird keine Zertifikatsvalidierung durchgeführt (ssl.CERT_NONE). Für eine sichere Verbindung müssen Sie das Zertifikat des Peers verifizieren, indem Sie cert_reqs / SSLContext.verify_mode auf ssl.CERT_REQUIRED setzen; andernfalls ist die Verbindung anfällig für Man-in-the-Middle-Angriffe.

CPythons wrap_socket gibt ein SSLSocket-Objekt zurück, das für Sockets typische Methoden wie send, recv usw. besitzt. MicroPythons wrap_socket gibt ein Objekt zurück, das eher CPythons SSLObject ähnelt und diese Socket-Methoden nicht besitzt.

verify_mode

Legt das Verhalten bei der Verifizierung von Peer-Zertifikaten fest oder gibt es zurück. Muss eine der CERT_*-Konstanten sein.

Bemerkung

ssl.CERT_REQUIRED setzt voraus, dass Datum/Uhrzeit des Geräts korrekt eingestellt sind, z. B. mit mpremote rtc --set oder ntptime, und auf der Clientseite muss server_hostname angegeben werden.

DTLS-Unterstützung

Unterschied zu CPython

Dies ist eine MicroPython-Erweiterung.

Dieses Modul unterstützt DTLS im Client- und Servermodus über die Konstanten PROTOCOL_DTLS_CLIENT und PROTOCOL_DTLS_SERVER, die als protocol-Argument von SSLContext verwendet werden können.

In diesem Fall wird erwartet, dass sich der zugrunde liegende Socket wie ein Datagramm-Socket verhält (d. h. wie der mit socket.socket mit socket.AF_INET als af und socket.SOCK_DGRAM als type geöffnete Socket).

DTLS-Serverunterstützung

Die DTLS-Serverunterstützung von MicroPython ist mit „Hello Verify“ konfiguriert, wie es für DTLS 1.2 erforderlich ist. Dies ist für DTLS-Clients transparent, es gibt jedoch relevante Aspekte bei der Implementierung eines DTLS-Servers in MicroPython zu beachten:

  • Der Server sollte beim Aufruf von SSLContext.wrap_socket() ein zusätzliches Argument client_id übergeben. Diese ID muss ein bytes-Objekt (oder ähnliches) mit einer transportspezifischen Kennung sein, die den Client repräsentiert.

    Der einfachste Ansatz besteht darin, das von socket.recv_from() zurückgegebene Tupel (client_ip, client_port) in eine Byte-Zeichenkette umzuwandeln, d. h.:

    _, client_addr = sock.recvfrom(1, socket.MSG_PEEK)
    sock.connect(client_addr)  # Connect back to the client
    sock = ssl_ctx.wrap_socket(sock, server_side=True,
                               client_id=repr(client_addr).encode())
    
  • Beim ersten Verbindungsversuch eines Clients schlägt der Aufruf von wrap_socket durch den Server mit einem OSError-Fehler „Hello Verify Required“ fehl. Das liegt daran, dass das DTLS-„Hello Verify“-Cookie dem Client noch nicht bekannt ist. Wenn sich derselbe Client ein zweites Mal verbindet, ist wrap_socket erfolgreich.

  • DTLS-Cookies für „Hello Verify“ sind dem SSLContext-Objekt zugeordnet, daher sollte dasselbe SSLContext-Objekt verwendet werden, um eine nachfolgende Verbindung desselben Clients zu umhüllen. Die Cookie-Implementierung beinhaltet ein Timeout und verbraucht unabhängig von der Anzahl der verbundenen Clients konstant viel Speicher, sodass es in Ordnung ist, dasselbe SSLContext-Objekt für die gesamte Lebensdauer des Servers wiederzuverwenden.

Konstanten

ssl.PROTOCOL_TLS_CLIENT: int

Unterstützter Wert für den Parameter protocol, der den TLS-Clientmodus auswählt.

ssl.PROTOCOL_TLS_SERVER: int

Unterstützter Wert für den Parameter protocol, der den TLS-Servermodus auswählt.

ssl.PROTOCOL_DTLS_CLIENT: int

Unterstützter Wert für den Parameter protocol, der den DTLS-Clientmodus auswählt.

ssl.PROTOCOL_DTLS_SERVER: int

Unterstützter Wert für den Parameter protocol, der den DTLS-Servermodus auswählt.

ssl.CERT_NONE: int

Unterstützter Wert für den Parameter cert_reqs und das Attribut SSLContext.verify_mode. Es wird keine Zertifikatsverifizierung des Peers durchgeführt.

ssl.CERT_OPTIONAL: int

Unterstützter Wert für den Parameter cert_reqs und das Attribut SSLContext.verify_mode. Die Zertifikatsverifizierung ist optional. Beachten Sie, dass sich dies auf der OpenMV Cam wie ssl.CERT_NONE verhält.

ssl.CERT_REQUIRED: int

Unterstützter Wert für den Parameter cert_reqs und das Attribut SSLContext.verify_mode. Vom Peer wird ein gültiges Zertifikat verlangt.