ssl — SSL/TLS-module

Deze module biedt toegang tot Transport Layer Security (voorheen en algemeen bekend als “Secure Sockets Layer”) encryptie- en peerauthenticatievoorzieningen voor netwerksockets, zowel aan de clientzijde als aan de serverzijde.

Tip

Nieuw met TLS op de camera? Begin met de Werken met TLS-certificaten-tutorial. Deze behandelt het kiezen van sleuteltypen, het maken en converteren van certificaten naar het DER-formaat dat de camera vereist, het op het apparaat krijgen ervan en het verifiëren van servers en clients – met volledige werkende voorbeelden.

Notitie

MicroPython implementeert ssl.SSLError niet. SSL/TLS-fouten worden in plaats daarvan opgeworpen als OSError.

Voorbeelden

TLS-client die het certificaat van de server verifieert aan de hand van een CA-certificaat (in DER-formaat) dat op het bestandssysteem is opgeslagen:

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()

Voor een snelle, onveilige verbinding (zonder certificaatvalidatie) kan in plaats daarvan de hulpfunctie ssl.wrap_socket() worden gebruikt:

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

TLS-server die zijn eigen certificaat en privésleutel presenteert (DER-formaat):

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()

Functies

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

Verpakt de opgegeven sock en geeft een nieuw wrapped-socket-object terug. De implementatie van deze functie maakt eerst een SSLContext aan en roept vervolgens de methode SSLContext.wrap_socket() aan op dat contextobject. De argumenten sock, server_side en server_hostname worden ongewijzigd doorgegeven aan de methodeaanroep. Het argument do_handshake wordt doorgegeven als do_handshake_on_connect. De overige argumenten hebben het volgende gedrag:

  • cert_reqs bepaalt of de peer (server of client) een geldig certificaat moet presenteren. Merk op dat ssl.CERT_NONE en ssl.CERT_OPTIONAL geen enkel certificaat valideren; alleen ssl.CERT_REQUIRED doet dat.

  • cadata is een bytes-object dat de CA-certificaatketen (in DER-formaat) bevat die het certificaat van de peer zal valideren. Momenteel wordt alleen één DER-gecodeerd certificaat ondersteund.

Classes

class ssl.SSLContext(protocol: int, /)

Maakt een nieuwe SSLContext-instantie aan. Het argument protocol moet een van de PROTOCOL_*-constanten zijn.

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

Laadt een privésleutel en het bijbehorende certificaat. certfile is een string met het bestandspad van het certificaat. keyfile is een string met het bestandspad van de privésleutel.

Verschil met CPython

MicroPython-uitbreiding: certfile en keyfile kunnen bytes-objecten zijn in plaats van strings, in welk geval ze worden geïnterpreteerd als de daadwerkelijke certificaat-/sleutelgegevens.

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

Laadt de CA-certificaatketen die het certificaat van de peer zal valideren. cafile is het bestandspad van de CA-certificaten. cadata is een bytes-object dat de CA-certificaten bevat. Slechts één van deze argumenten mag worden opgegeven.

get_ciphers() List[str]

Geeft een lijst met ingeschakelde ciphers terug, als een lijst van strings.

set_ciphers(ciphers: List[str]) None

Stelt de beschikbare ciphers in voor sockets die met deze context worden aangemaakt. ciphers moet een lijst van strings zijn in de IANA cipher suite-indeling .

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

Neemt een stream sock (meestal een socket.socket-instantie van het type SOCK_STREAM) en geeft een instantie van ssl.SSLSocket terug die de onderliggende stream verpakt. Het teruggegeven object heeft de gebruikelijke stream-interfacemethoden zoals read(), write(), enz.

  • server_side selecteert of de verpakte socket zich aan de server- of clientzijde bevindt. Een server-side SSL-socket moet worden aangemaakt vanuit een normale socket die wordt teruggegeven door accept() op een niet-SSL luisterende serversocket.

  • do_handshake_on_connect bepaalt of de handshake wordt uitgevoerd als onderdeel van de wrap_socket of dat deze wordt uitgesteld tot de eerste leesacties of schrijfacties. Voor blokkerende sockets is het direct uitvoeren van de handshake standaard. Voor niet-blokkerende sockets (d.w.z. wanneer de sock die aan wrap_socket wordt doorgegeven in niet-blokkerende modus staat) moet de handshake in het algemeen worden uitgesteld, omdat wrap_socket anders blokkeert totdat deze is voltooid.

  • server_hostname is bedoeld voor gebruik als client en stelt de hostnaam in om te controleren tegen het ontvangen servercertificaat. Het stelt ook de naam in voor Server Name Indication (SNI), waardoor de server het juiste certificaat kan presenteren.

  • client_id is een MicroPython-specifiek uitbreidingsargument dat alleen wordt gebruikt bij het implementeren van een DTLS-server. Zie DTLS-ondersteuning voor details.

Waarschuwing

Standaard wordt er geen certificaatvalidatie uitgevoerd (ssl.CERT_NONE). Voor een beveiligde verbinding moet je het certificaat van de peer verifiëren door cert_reqs / SSLContext.verify_mode in te stellen op ssl.CERT_REQUIRED; anders is de verbinding kwetsbaar voor man-in-the-middle-aanvallen.

CPython’s wrap_socket geeft een SSLSocket-object terug dat methoden heeft die typisch zijn voor sockets, zoals send, recv, enz. MicroPython’s wrap_socket geeft een object terug dat meer lijkt op CPython’s SSLObject, dat deze socketmethoden niet heeft.

verify_mode

Stelt het gedrag voor de verificatie van peercertificaten in of vraagt het op. Moet een van de CERT_*-constanten zijn.

Notitie

ssl.CERT_REQUIRED vereist dat de datum/tijd van het apparaat correct is ingesteld, bijv. met mpremote rtc --set of ntptime, en server_hostname moet aan de clientzijde worden opgegeven.

DTLS-ondersteuning

Verschil met CPython

Dit is een MicroPython-uitbreiding.

Deze module ondersteunt DTLS in client- en servermodus via de constanten PROTOCOL_DTLS_CLIENT en PROTOCOL_DTLS_SERVER, die kunnen worden gebruikt als het protocol-argument van SSLContext.

In dit geval wordt verwacht dat de onderliggende socket zich gedraagt als een datagram-socket (d.w.z. zoals de socket die wordt geopend met socket.socket met socket.AF_INET als af en socket.SOCK_DGRAM als type).

DTLS-serverondersteuning

MicroPython’s DTLS-serverondersteuning is geconfigureerd met “Hello Verify” zoals vereist voor DTLS 1.2. Dit is transparant voor DTLS-clients, maar er zijn relevante overwegingen bij het implementeren van een DTLS-server in MicroPython:

  • De server moet een extra argument client_id doorgeven bij het aanroepen van SSLContext.wrap_socket(). Deze ID moet een bytes-object (of vergelijkbaar) zijn met een transportspecifieke identificatie die de client vertegenwoordigt.

    De eenvoudigste aanpak is om de tuple (client_ip, client_port) die wordt teruggegeven door socket.recv_from() om te zetten in een byte-string, d.w.z.:

    _, 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())
    
  • De eerste keer dat een client verbinding maakt, zal de serveraanroep van wrap_socket mislukken met een OSError-fout “Hello Verify Required”. Dit komt doordat de DTLS “Hello Verify”-cookie nog niet bekend is bij de client. Als dezelfde client een tweede keer verbinding maakt, dan zal wrap_socket slagen.

  • DTLS-cookies voor “Hello Verify” zijn gekoppeld aan het SSLContext-object, dus hetzelfde SSLContext-object moet worden gebruikt om een volgende verbinding van dezelfde client te verpakken. De cookie-implementatie bevat een time-out en heeft een constant geheugengebruik, ongeacht hoeveel clients verbinding maken, dus het is prima om hetzelfde SSLContext-object te hergebruiken gedurende de levensduur van de server.

Constanten

ssl.PROTOCOL_TLS_CLIENT: int

Ondersteunde waarde voor de parameter protocol, die de TLS-clientmodus selecteert.

ssl.PROTOCOL_TLS_SERVER: int

Ondersteunde waarde voor de parameter protocol, die de TLS-servermodus selecteert.

ssl.PROTOCOL_DTLS_CLIENT: int

Ondersteunde waarde voor de parameter protocol, die de DTLS-clientmodus selecteert.

ssl.PROTOCOL_DTLS_SERVER: int

Ondersteunde waarde voor de parameter protocol, die de DTLS-servermodus selecteert.

ssl.CERT_NONE: int

Ondersteunde waarde voor de parameter cert_reqs en het attribuut SSLContext.verify_mode. Er wordt geen certificaatverificatie op de peer uitgevoerd.

ssl.CERT_OPTIONAL: int

Ondersteunde waarde voor de parameter cert_reqs en het attribuut SSLContext.verify_mode. Certificaatverificatie is optioneel. Merk op dat dit op de OpenMV Cam zich gedraagt als ssl.CERT_NONE.

ssl.CERT_REQUIRED: int

Ondersteunde waarde voor de parameter cert_reqs en het attribuut SSLContext.verify_mode. Er is een geldig certificaat van de peer vereist.