ssl — modulul SSL/TLS¶
Acest modul oferă acces la facilitățile de criptare și de autentificare a partenerilor de tip Transport Layer Security (cunoscut anterior și pe scară largă drept „Secure Sockets Layer”) pentru socket-uri de rețea, atât pe partea de client, cât și pe partea de server.
Sfat
Lucrezi pentru prima dată cu TLS pe cameră? Începe cu tutorialul Lucrul cu certificate TLS. Acesta te ghidează prin alegerea tipurilor de chei, crearea și conversia certificatelor în formatul DER cerut de cameră, transferul lor pe dispozitiv și verificarea serverelor și a clienților – cu exemple complete și funcționale.
Notă
MicroPython nu implementează ssl.SSLError. Eșecurile SSL/TLS sunt generate în schimb ca OSError.
Exemple¶
Client TLS, care verifică certificatul serverului față de un certificat CA (în format DER) stocat pe sistemul de fișiere:
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()
Pentru o conexiune rapidă și nesigură (fără validarea certificatului) se poate folosi în schimb funcția de comoditate ssl.wrap_socket()
ssock = ssl.wrap_socket(sock, server_hostname="example.com")
Server TLS, care prezintă propriul certificat și cheie privată (format 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()
Funcții¶
- 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¶
Înfășoară sock-ul dat și returnează un nou obiect wrapped-socket. Implementarea acestei funcții constă în a crea mai întâi un
SSLContextși apoi a apela metodaSSLContext.wrap_socket()pe acel obiect context. Argumentele sock, server_side și server_hostname sunt transmise neschimbate către apelul metodei. Argumentul do_handshake este transmis ca do_handshake_on_connect. Argumentele rămase au următorul comportament:cert_reqs determină dacă partenerul (serverul sau clientul) trebuie să prezinte un certificat valid. Reține că
ssl.CERT_NONEșissl.CERT_OPTIONALnu validează niciun certificat; numaissl.CERT_REQUIREDface acest lucru.cadata este un obiect bytes care conține lanțul de certificate CA (în format DER) ce va valida certificatul partenerului. În prezent este acceptat doar un singur certificat codificat DER.
Clase¶
- class ssl.SSLContext(protocol: int, /)¶
Creează o nouă instanță SSLContext. Argumentul protocol trebuie să fie una dintre constantele
PROTOCOL_*.- load_cert_chain(certfile: str | bytes, keyfile: str | bytes) None¶
Încarcă o cheie privată și certificatul corespunzător. certfile este un șir cu calea fișierului certificatului. keyfile este un șir cu calea fișierului cheii private.
Diferență față de CPython
Extensie MicroPython: certfile și keyfile pot fi obiecte bytes în loc de șiruri, caz în care sunt interpretate ca datele efective ale certificatului/cheii.
- load_verify_locations(cafile: str | None = None, cadata: bytes | None = None) None¶
Încarcă lanțul de certificate CA care va valida certificatul partenerului. cafile este calea fișierului cu certificatele CA. cadata este un obiect bytes care conține certificatele CA. Trebuie furnizat doar unul dintre aceste argumente.
- set_ciphers(ciphers: List[str]) None¶
Setează cifrurile disponibile pentru socket-urile create cu acest context. ciphers trebuie să fie o listă de șiruri în formatul IANA pentru suite de cifruri .
- 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¶
Preia un stream sock (de obicei o instanță socket.socket de tip
SOCK_STREAM) și returnează o instanță de ssl.SSLSocket, care înfășoară stream-ul subiacent. Obiectul returnat are metodele uzuale ale interfeței stream, precumread(),write()etc.server_side selectează dacă socket-ul înfășurat se află pe partea de server sau de client. Un socket SSL pe partea de server ar trebui creat dintr-un socket normal returnat de
accept()pe un socket de server non-SSL aflat în ascultare.do_handshake_on_connect determină dacă handshake-ul este efectuat ca parte a
wrap_socketsau dacă este amânat pentru a fi efectuat ca parte a citirilor sau scrierilor inițiale. Pentru socket-urile cu blocare, efectuarea imediată a handshake-ului este practica standard. Pentru socket-urile fără blocare (adică atunci când sock-ul transmis cătrewrap_socketeste în mod fără blocare), handshake-ul ar trebui de regulă amânat, deoarece altfelwrap_socketse blochează până la finalizarea acestuia.server_hostname este destinat utilizării ca client și setează numele de gazdă care va fi verificat față de certificatul de server primit. De asemenea, setează numele pentru Server Name Indication (SNI), permițând serverului să prezinte certificatul corespunzător.
client_id este un argument de extensie specific MicroPython, folosit numai la implementarea unui server DTLS. Vezi Suport DTLS pentru detalii.
Atenționare
În mod implicit nu se efectuează nicio validare a certificatului (
ssl.CERT_NONE). Pentru o conexiune sigură trebuie să verifici certificatul partenerului setând cert_reqs /SSLContext.verify_modelassl.CERT_REQUIRED; în caz contrar, conexiunea este vulnerabilă la atacuri de tip man-in-the-middle.Funcția
wrap_socketdin CPython returnează un obiectSSLSocketcare are metode tipice pentru socket-uri, precumsend,recvetc. Funcțiawrap_socketdin MicroPython returnează un obiect mai asemănător cuSSLObjectdin CPython, care nu are aceste metode de socket.
- verify_mode¶
Setează sau obține comportamentul de verificare a certificatelor partenerilor. Trebuie să fie una dintre constantele
CERT_*.Notă
ssl.CERT_REQUIREDnecesită ca data/ora dispozitivului să fie setate corect, de exemplu folosind mpremote rtc --set sauntptime, iarserver_hostnametrebuie specificat atunci când se află pe partea de client.
Suport DTLS¶
Diferență față de CPython
Aceasta este o extensie MicroPython.
Acest modul acceptă DTLS în mod client și server prin constantele PROTOCOL_DTLS_CLIENT și PROTOCOL_DTLS_SERVER, care pot fi folosite ca argument protocol al SSLContext.
În acest caz se așteaptă ca socket-ul subiacent să se comporte ca un socket de datagrame (adică precum socket-ul deschis cu socket.socket având socket.AF_INET ca af și socket.SOCK_DGRAM ca type).
Suport pentru server DTLS¶
Suportul de server DTLS din MicroPython este configurat cu „Hello Verify”, așa cum este cerut pentru DTLS 1.2. Acest lucru este transparent pentru clienții DTLS, dar există considerații relevante atunci când implementezi un server DTLS în MicroPython:
Serverul ar trebui să transmită un argument suplimentar client_id la apelarea
SSLContext.wrap_socket(). Acest ID trebuie să fie un obiectbytes(sau similar) cu un identificator specific transportului care reprezintă clientul.Cea mai simplă abordare este de a converti tuplul
(client_ip, client_port)returnat desocket.recv_from()într-un șir de octeți, adică:_, 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())
Prima dată când un client se conectează, apelul serverului către
wrap_socketva eșua cu o eroareOSError„Hello Verify Required”. Acest lucru se întâmplă deoarece cookie-ul DTLS „Hello Verify” nu este încă cunoscut de client. Dacă același client se conectează a doua oară, atunciwrap_socketva reuși.Cookie-urile DTLS pentru „Hello Verify” sunt asociate cu obiectul
SSLContext, așa că același obiectSSLContextar trebui folosit pentru a înfășura o conexiune ulterioară de la același client. Implementarea cookie-ului include un timeout și are un consum de memorie constant, indiferent câți clienți se conectează, așa că este în regulă să reutilizezi același obiectSSLContextpe toată durata de viață a serverului.
Constante¶
- ssl.PROTOCOL_TLS_CLIENT: int¶
Valoare acceptată pentru parametrul protocol, care selectează modul client TLS.
- ssl.PROTOCOL_TLS_SERVER: int¶
Valoare acceptată pentru parametrul protocol, care selectează modul server TLS.
- ssl.PROTOCOL_DTLS_CLIENT: int¶
Valoare acceptată pentru parametrul protocol, care selectează modul client DTLS.
- ssl.PROTOCOL_DTLS_SERVER: int¶
Valoare acceptată pentru parametrul protocol, care selectează modul server DTLS.
- ssl.CERT_NONE: int¶
Valoare acceptată pentru parametrul cert_reqs și pentru atributul
SSLContext.verify_mode. Nu se efectuează nicio verificare a certificatului partenerului.
- ssl.CERT_OPTIONAL: int¶
Valoare acceptată pentru parametrul cert_reqs și pentru atributul
SSLContext.verify_mode. Verificarea certificatului este opțională. Reține că pe OpenMV Cam aceasta se comportă cassl.CERT_NONE.
- ssl.CERT_REQUIRED: int¶
Valoare acceptată pentru parametrul cert_reqs și pentru atributul
SSLContext.verify_mode. De la partener se cere un certificat valid.