ssl — modul SSL/TLS

Tento modul poskytuje přístup k šifrování Transport Layer Security (dříve a obecně známému jako „Secure Sockets Layer“) a k mechanismům ověřování protistrany pro síťové sockety, a to jak na straně klienta, tak na straně serveru.

Tip

Začínáte s TLS na kameře? Začněte tutoriálem Práce s TLS certifikáty. Provede vás výběrem typů klíčů, vytvořením a převodem certifikátů do formátu DER, který kamera vyžaduje, jejich přenosem do zařízení a ověřováním serverů i klientů – včetně kompletních funkčních příkladů.

Poznámka

MicroPython neimplementuje ssl.SSLError. Chyby SSL/TLS jsou místo toho vyvolávány jako OSError.

Příklady

TLS klient, ověřující certifikát serveru proti certifikátu CA (ve formátu DER) uloženému v souborovém systému:

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

Pro rychlé, nezabezpečené připojení (bez ověřování certifikátu) lze místo toho použít pomocnou funkci ssl.wrap_socket()

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

TLS server, předkládající svůj vlastní certifikát a soukromý klíč (formát DER):

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

Funkce

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

Obalí daný sock a vrátí nový objekt obaleného socketu. Implementace této funkce nejprve vytvoří SSLContext a poté nad tímto kontextovým objektem zavolá metodu SSLContext.wrap_socket(). Argumenty sock, server_side a server_hostname jsou beze změny předány volání metody. Argument do_handshake je předán jako do_handshake_on_connect. Zbývající argumenty mají následující chování:

  • cert_reqs určuje, zda protistrana (server nebo klient) musí předložit platný certifikát. Vezměte na vědomí, že ssl.CERT_NONE a ssl.CERT_OPTIONAL neověřují žádný certifikát; činí tak pouze ssl.CERT_REQUIRED.

  • cadata je objekt typu bytes obsahující řetězec certifikátů CA (ve formátu DER), který bude ověřovat certifikát protistrany. Aktuálně je podporován pouze jediný certifikát zakódovaný v DER.

Třídy

class ssl.SSLContext(protocol: int, /)

Vytvoří novou instanci SSLContext. Argument protocol musí být jednou z konstant PROTOCOL_*.

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

Načte soukromý klíč a odpovídající certifikát. certfile je řetězec s cestou k souboru certifikátu. keyfile je řetězec s cestou k souboru soukromého klíče.

Rozdíl oproti CPythonu

Rozšíření MicroPythonu: certfile a keyfile mohou být místo řetězců objekty typu bytes, v takovém případě jsou interpretovány jako vlastní data certifikátu/klíče.

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

Načte řetězec certifikátů CA, který bude ověřovat certifikát protistrany. cafile je cesta k souboru certifikátů CA. cadata je objekt typu bytes obsahující certifikáty CA. Měl by být uveden pouze jeden z těchto argumentů.

get_ciphers() List[str]

Získá seznam povolených šifer, vrácený jako seznam řetězců.

set_ciphers(ciphers: List[str]) None

Nastaví dostupné šifry pro sockety vytvořené s tímto kontextem. ciphers by měl být seznam řetězců ve formátu sad šifer IANA .

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

Přijímá stream sock (obvykle instanci socket.socket typu SOCK_STREAM) a vrací instanci ssl.SSLSocket, která obaluje podkladový stream. Vrácený objekt má obvyklé metody rozhraní stream, jako read(), write() atd.

  • server_side vybírá, zda je obalený socket na straně serveru, nebo klienta. SSL socket na straně serveru by měl být vytvořen z běžného socketu vráceného metodou accept() na naslouchajícím serverovém socketu bez SSL.

  • do_handshake_on_connect určuje, zda se handshake provede jako součást wrap_socket, nebo zda se jeho provedení odloží na první čtení či zápisy. Pro blokující sockety je standardem provést handshake okamžitě. Pro neblokující sockety (tj. když je sock předaný do wrap_socket v neblokujícím režimu) by měl být handshake obvykle odložen, protože jinak wrap_socket blokuje, dokud se nedokončí.

  • server_hostname slouží pro použití na straně klienta a nastavuje název hostitele, který se má kontrolovat proti přijatému certifikátu serveru. Nastavuje také název pro Server Name Indication (SNI), což serveru umožňuje předložit správný certifikát.

  • client_id je argument specifický pro MicroPython, používaný pouze při implementaci DTLS serveru. Podrobnosti viz Podpora DTLS.

Varování

Ve výchozím nastavení není prováděno žádné ověřování certifikátu (ssl.CERT_NONE). Pro zabezpečené připojení musíte ověřit certifikát protistrany nastavením cert_reqs / SSLContext.verify_mode na ssl.CERT_REQUIRED; jinak je připojení zranitelné vůči útokům typu man-in-the-middle.

wrap_socket v CPythonu vrací objekt SSLSocket, který má metody typické pro sockety, jako send, recv atd. wrap_socket v MicroPythonu vrací objekt podobnější objektu SSLObject z CPythonu, který tyto metody socketu nemá.

verify_mode

Nastaví nebo získá chování ověřování certifikátů protistrany. Musí být jednou z konstant CERT_*.

Poznámka

ssl.CERT_REQUIRED vyžaduje, aby bylo na zařízení správně nastaveno datum/čas, např. pomocí mpremote rtc --set nebo ntptime, a na straně klienta musí být uveden server_hostname.

Podpora DTLS

Rozdíl oproti CPythonu

Toto je rozšíření MicroPythonu.

Tento modul podporuje DTLS v klientském i serverovém režimu prostřednictvím konstant PROTOCOL_DTLS_CLIENT a PROTOCOL_DTLS_SERVER, které lze použít jako argument protocol třídy SSLContext.

V tomto případě se očekává, že se podkladový socket chová jako datagramový socket (tj. jako socket otevřený pomocí socket.socket s socket.AF_INET jako af a socket.SOCK_DGRAM jako type).

Podpora DTLS serveru

Podpora DTLS serveru v MicroPythonu je nakonfigurována s „Hello Verify“, jak vyžaduje DTLS 1.2. To je pro DTLS klienty transparentní, ale při implementaci DTLS serveru v MicroPythonu je třeba vzít v úvahu několik souvislostí:

  • Server by měl při volání SSLContext.wrap_socket() předat dodatečný argument client_id. Tímto ID musí být objekt typu bytes (nebo podobný) s identifikátorem specifickým pro daný transport, který reprezentuje klienta.

    Nejjednodušší přístup je převést na bajtový řetězec n-tici (client_ip, client_port) vrácenou z socket.recv_from(), tj.:

    _, 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())
    
  • Při prvním připojení klienta volání wrap_socket na serveru selže s chybou OSError „Hello Verify Required“. Je to proto, že DTLS cookie „Hello Verify“ klient zatím nezná. Pokud se tentýž klient připojí podruhé, wrap_socket uspěje.

  • DTLS cookies pro „Hello Verify“ jsou asociovány s objektem SSLContext, takže k obalení následného připojení od téhož klienta by měl být použit tentýž objekt SSLContext. Implementace cookies zahrnuje časový limit a má konstantní spotřebu paměti bez ohledu na to, kolik klientů se připojí, takže je v pořádku používat tentýž objekt SSLContext po celou dobu životnosti serveru.

Konstanty

ssl.PROTOCOL_TLS_CLIENT: int

Podporovaná hodnota parametru protocol, vybírající klientský režim TLS.

ssl.PROTOCOL_TLS_SERVER: int

Podporovaná hodnota parametru protocol, vybírající serverový režim TLS.

ssl.PROTOCOL_DTLS_CLIENT: int

Podporovaná hodnota parametru protocol, vybírající klientský režim DTLS.

ssl.PROTOCOL_DTLS_SERVER: int

Podporovaná hodnota parametru protocol, vybírající serverový režim DTLS.

ssl.CERT_NONE: int

Podporovaná hodnota parametru cert_reqs a atributu SSLContext.verify_mode. U protistrany se neprovádí žádné ověřování certifikátu.

ssl.CERT_OPTIONAL: int

Podporovaná hodnota parametru cert_reqs a atributu SSLContext.verify_mode. Ověření certifikátu je volitelné. Vezměte na vědomí, že na OpenMV Cam se chová jako ssl.CERT_NONE.

ssl.CERT_REQUIRED: int

Podporovaná hodnota parametru cert_reqs a atributu SSLContext.verify_mode. Od protistrany je vyžadován platný certifikát.