ssl — SSL/TLS modul

Ovaj modul omogućuje pristup mehanizmima za šifriranje i provjeru identiteta na razini Transport Layer Security (ranije i šire poznato kao “Secure Sockets Layer”) za mrežne socket-e, kako na strani klijenta tako i na strani poslužitelja.

Savjet

Nov si u radu s TLS-om na kameri? Započni s vodičem Rad s TLS certifikatima. On vodi kroz odabir vrsta ključeva, stvaranje i pretvaranje certifikata u DER format koji kamera zahtijeva, prijenos na uređaj te provjeru poslužitelja i klijenata – s potpunim radnim primjerima.

Napomena

MicroPython ne implementira ssl.SSLError. Pogreške SSL/TLS-a umjesto toga se dižu kao OSError.

Primjeri

TLS klijent koji provjerava certifikat poslužitelja u odnosu na CA certifikat (u DER formatu) pohranjen na datotečnom sustavu:

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

Za brzu, nesigurnu vezu (bez provjere certifikata) umjesto toga može se upotrijebiti praktična funkcija ssl.wrap_socket()

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

TLS poslužitelj koji predstavlja vlastiti certifikat i privatni ključ (DER format):

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

Funkcije

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

Omata zadani sock i vraća novi omotani socket objekt. Implementacija ove funkcije najprije stvara SSLContext, a zatim na tom kontekstnom objektu poziva metodu SSLContext.wrap_socket(). Argumenti sock, server_side i server_hostname nepromijenjeni se prosljeđuju u poziv metode. Argument do_handshake prosljeđuje se kao do_handshake_on_connect. Preostali argumenti imaju sljedeće ponašanje:

  • cert_reqs određuje mora li druga strana (poslužitelj ili klijent) predočiti valjani certifikat. Imajte na umu da ssl.CERT_NONE i ssl.CERT_OPTIONAL ne provjeravaju nijedan certifikat; to čini samo ssl.CERT_REQUIRED.

  • cadata je bytes objekt koji sadrži lanac CA certifikata (u DER formatu) koji će provjeriti certifikat druge strane. Trenutačno je podržan samo jedan DER-kodirani certifikat.

Klase

class ssl.SSLContext(protocol: int, /)

Stvara novu instancu SSLContext. Argument protocol mora biti jedna od konstanti PROTOCOL_*.

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

Učitava privatni ključ i odgovarajući certifikat. certfile je niz znakova s putanjom datoteke certifikata. keyfile je niz znakova s putanjom datoteke privatnog ključa.

Razlika u odnosu na CPython

MicroPython proširenje: certfile i keyfile mogu biti bytes objekti umjesto nizova znakova, u kojem se slučaju tumače kao stvarni podaci certifikata/ključa.

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

Učitava lanac CA certifikata koji će provjeriti certifikat druge strane. cafile je putanja datoteke CA certifikata. cadata je bytes objekt koji sadrži CA certifikate. Treba navesti samo jedan od ovih argumenata.

get_ciphers() List[str]

Dohvaća popis omogućenih šifri, vraćen kao popis nizova znakova.

set_ciphers(ciphers: List[str]) None

Postavlja dostupne šifre za socket-e stvorene s ovim kontekstom. ciphers treba biti popis nizova znakova u IANA formatu šifrarnih kompleta .

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

Prima stream sock (obično instancu socket.socket tipa SOCK_STREAM) i vraća instancu ssl.SSLSocket koja omata temeljni tok. Vraćeni objekt ima uobičajene metode sučelja stream poput read(), write() itd.

  • server_side odabire je li omotani socket na strani poslužitelja ili klijenta. SSL socket na strani poslužitelja treba stvoriti iz normalnog socket-a vraćenog iz accept() na ne-SSL slušajućem poslužiteljskom socket-u.

  • do_handshake_on_connect određuje izvodi li se rukovanje (handshake) kao dio wrap_socket ili se odgađa kako bi se izvelo u sklopu početnog čitanja ili pisanja. Za blokirajuće socket-e standardno je rukovanje izvesti odmah. Za neblokirajuće socket-e (tj. kada je sock proslijeđen u wrap_socket u neblokirajućem načinu rada) rukovanje treba općenito odgoditi jer u protivnom wrap_socket blokira dok se ne dovrši.

  • server_hostname služi za uporabu kao klijent i postavlja ime poslužitelja koje se provjerava u odnosu na primljeni certifikat poslužitelja. Također postavlja ime za Server Name Indication (SNI), omogućujući poslužitelju da predoči ispravan certifikat.

  • client_id je argument proširenja specifičan za MicroPython koji se koristi samo pri implementaciji DTLS poslužitelja. Pojedinosti potražite u Podrška za DTLS.

Upozorenje

Prema zadanim postavkama ne provodi se provjera certifikata (ssl.CERT_NONE). Za sigurnu vezu morate provjeriti certifikat druge strane postavljanjem cert_reqs / SSLContext.verify_mode na ssl.CERT_REQUIRED; u protivnom je veza ranjiva na napade tipa man-in-the-middle.

CPython-ov wrap_socket vraća objekt SSLSocket koji ima metode tipične za socket-e, kao što su send, recv itd. MicroPython-ov wrap_socket vraća objekt sličniji CPython-ovom SSLObject koji nema te socket metode.

verify_mode

Postavlja ili dohvaća ponašanje za provjeru certifikata druge strane. Mora biti jedna od konstanti CERT_*.

Napomena

ssl.CERT_REQUIRED zahtijeva da su datum/vrijeme uređaja ispravno postavljeni, npr. pomoću mpremote rtc --set ili ntptime, a server_hostname mora biti naveden kada se radi na strani klijenta.

Podrška za DTLS

Razlika u odnosu na CPython

Ovo je MicroPython proširenje.

Ovaj modul podržava DTLS u klijentskom i poslužiteljskom načinu rada putem konstanti PROTOCOL_DTLS_CLIENT i PROTOCOL_DTLS_SERVER koje se mogu upotrijebiti kao argument protocol u SSLContext.

U tom se slučaju očekuje da se temeljni socket ponaša kao datagramski socket (tj. poput socket-a otvorenog s socket.socket uz socket.AF_INET kao af i socket.SOCK_DGRAM kao type).

Podrška za DTLS poslužitelj

MicroPython-ova podrška za DTLS poslužitelj konfigurirana je s „Hello Verify” kako to zahtijeva DTLS 1.2. To je prozirno za DTLS klijente, ali postoje važna razmatranja pri implementaciji DTLS poslužitelja u MicroPython-u:

  • Poslužitelj treba proslijediti dodatni argument client_id pri pozivu SSLContext.wrap_socket(). Taj ID mora biti bytes objekt (ili sličan) s identifikatorom specifičnim za prijenos koji predstavlja klijenta.

    Najjednostavniji je pristup pretvoriti torku (client_ip, client_port) koju vraća socket.recv_from() u niz bajtova, 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())
    
  • Prvi put kada se klijent poveže, poslužiteljev poziv funkciji wrap_socket neuspjet će s pogreškom OSError „Hello Verify Required”. To je zato što DTLS „Hello Verify” kolačić klijentu još nije poznat. Ako se isti klijent poveže drugi put, wrap_socket će uspjeti.

  • DTLS kolačići za „Hello Verify” povezani su s objektom SSLContext, pa za omatanje sljedeće veze od istog klijenta treba upotrijebiti isti objekt SSLContext. Implementacija kolačića uključuje istek vremena i ima konstantnu uporabu memorije bez obzira na to koliko se klijenata poveže, pa je u redu ponovno koristiti isti objekt SSLContext tijekom cijelog životnog vijeka poslužitelja.

Konstante

ssl.PROTOCOL_TLS_CLIENT: int

Podržana vrijednost za parametar protocol, koja odabire TLS klijentski način rada.

ssl.PROTOCOL_TLS_SERVER: int

Podržana vrijednost za parametar protocol, koja odabire TLS poslužiteljski način rada.

ssl.PROTOCOL_DTLS_CLIENT: int

Podržana vrijednost za parametar protocol, koja odabire DTLS klijentski način rada.

ssl.PROTOCOL_DTLS_SERVER: int

Podržana vrijednost za parametar protocol, koja odabire DTLS poslužiteljski način rada.

ssl.CERT_NONE: int

Podržana vrijednost za parametar cert_reqs i atribut SSLContext.verify_mode. Ne provodi se provjera certifikata druge strane.

ssl.CERT_OPTIONAL: int

Podržana vrijednost za parametar cert_reqs i atribut SSLContext.verify_mode. Provjera certifikata je neobavezna. Imajte na umu da se na OpenMV Cam ovo ponaša kao ssl.CERT_NONE.

ssl.CERT_REQUIRED: int

Podržana vrijednost za parametar cert_reqs i atribut SSLContext.verify_mode. Od druge strane zahtijeva se valjani certifikat.