ssl — modulo SSL/TLS¶
Questo modulo fornisce l’accesso alle funzionalità di crittografia e autenticazione dei peer del Transport Layer Security (precedentemente e ampiamente noto come “Secure Sockets Layer”) per i socket di rete, sia lato client che lato server.
Suggerimento
Sei alle prime armi con TLS sulla camera? Inizia dal tutorial Lavorare con i certificati TLS. Ti guida nella scelta dei tipi di chiave, nella creazione e conversione dei certificati nel formato DER richiesto dalla camera, nel trasferimento sul dispositivo e nella verifica di server e client – con esempi completi e funzionanti.
Nota
MicroPython non implementa ssl.SSLError. I fallimenti SSL/TLS vengono invece sollevati come OSError.
Esempi¶
Client TLS, che verifica il certificato del server rispetto a un certificato CA (in formato DER) memorizzato sul filesystem:
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()
Per una connessione rapida e non sicura (senza convalida del certificato) è possibile usare invece la funzione di comodità ssl.wrap_socket()
ssock = ssl.wrap_socket(sock, server_hostname="example.com")
Server TLS, che presenta il proprio certificato e la chiave privata (formato 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()
Funzioni¶
- 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¶
Incapsula il sock fornito e restituisce un nuovo oggetto wrapped-socket. L’implementazione di questa funzione consiste nel creare prima un
SSLContexte poi chiamare il metodoSSLContext.wrap_socket()su tale oggetto context. Gli argomenti sock, server_side e server_hostname vengono passati invariati alla chiamata del metodo. L’argomento do_handshake viene passato come do_handshake_on_connect. Gli argomenti rimanenti hanno il seguente comportamento:cert_reqs determina se il peer (server o client) debba presentare un certificato valido. Nota che
ssl.CERT_NONEessl.CERT_OPTIONALnon convalidano alcun certificato; solossl.CERT_REQUIREDlo fa.cadata è un oggetto bytes che contiene la catena di certificati CA (in formato DER) che convaliderà il certificato del peer. Attualmente è supportato un solo certificato codificato in DER.
Classi¶
- class ssl.SSLContext(protocol: int, /)¶
Crea una nuova istanza di SSLContext. L’argomento protocol deve essere una delle costanti
PROTOCOL_*.- load_cert_chain(certfile: str | bytes, keyfile: str | bytes) None¶
Carica una chiave privata e il certificato corrispondente. certfile è una stringa con il percorso del file del certificato. keyfile è una stringa con il percorso del file della chiave privata.
Differenza rispetto a CPython
Estensione di MicroPython: certfile e keyfile possono essere oggetti bytes anziché stringhe, nel qual caso vengono interpretati come i dati effettivi del certificato/della chiave.
- load_verify_locations(cafile: str | None = None, cadata: bytes | None = None) None¶
Carica la catena di certificati CA che convaliderà il certificato del peer. cafile è il percorso del file dei certificati CA. cadata è un oggetto bytes che contiene i certificati CA. Deve essere fornito solo uno di questi argomenti.
- get_ciphers() List[str]¶
Ottiene un elenco delle cifre abilitate, restituito come elenco di stringhe.
- set_ciphers(ciphers: List[str]) None¶
Imposta le cifre disponibili per i socket creati con questo context. ciphers deve essere un elenco di stringhe nel formato IANA cipher suite .
- 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¶
Prende uno sock di tipo stream (di solito un’istanza socket.socket di tipo
SOCK_STREAM) e restituisce un’istanza di ssl.SSLSocket, che incapsula lo stream sottostante. L’oggetto restituito ha i consueti metodi dell’interfaccia stream comeread(),write(), ecc.server_side seleziona se il socket incapsulato si trovi lato server o lato client. Un socket SSL lato server dovrebbe essere creato a partire da un socket normale restituito da
accept()su un socket di ascolto non SSL.do_handshake_on_connect determina se l’handshake venga eseguito come parte di
wrap_socketo se venga differito per essere eseguito come parte delle letture o scritture iniziali. Per i socket bloccanti è standard eseguire l’handshake immediatamente. Per i socket non bloccanti (cioè quando il sock passato awrap_socketè in modalità non bloccante) l’handshake dovrebbe generalmente essere differito, altrimentiwrap_socketsi blocca fino al suo completamento.server_hostname è destinato all’uso come client e imposta il nome host da verificare rispetto al certificato del server ricevuto. Imposta inoltre il nome per il Server Name Indication (SNI), consentendo al server di presentare il certificato corretto.
client_id è un argomento di estensione specifico di MicroPython usato solo quando si implementa un server DTLS. Vedi Supporto DTLS per i dettagli.
Avvertimento
Per impostazione predefinita non viene eseguita alcuna convalida del certificato (
ssl.CERT_NONE). Per una connessione sicura devi verificare il certificato del peer impostando cert_reqs /SSLContext.verify_modesussl.CERT_REQUIRED; in caso contrario la connessione è vulnerabile ad attacchi man-in-the-middle.Il
wrap_socketdi CPython restituisce un oggettoSSLSocketche possiede metodi tipici dei socket, comesend,recv, ecc. Ilwrap_socketdi MicroPython restituisce un oggetto più simile all”SSLObjectdi CPython, che non possiede questi metodi dei socket.
- verify_mode¶
Imposta o ottiene il comportamento per la verifica dei certificati dei peer. Deve essere una delle costanti
CERT_*.Nota
ssl.CERT_REQUIREDrichiede che la data/ora del dispositivo sia impostata correttamente, ad esempio usando mpremote rtc --set ontptime, e cheserver_hostnamesia specificato quando si è lato client.
Supporto DTLS¶
Differenza rispetto a CPython
Questa è un’estensione di MicroPython.
Questo modulo supporta DTLS in modalità client e server tramite le costanti PROTOCOL_DTLS_CLIENT e PROTOCOL_DTLS_SERVER che possono essere usate come argomento protocol di SSLContext.
In questo caso ci si aspetta che il socket sottostante si comporti come un socket datagram (cioè come il socket aperto con socket.socket con socket.AF_INET come af e socket.SOCK_DGRAM come type).
Supporto server DTLS¶
Il supporto server DTLS di MicroPython è configurato con «Hello Verify» come richiesto per DTLS 1.2. Questo è trasparente per i client DTLS, ma vi sono considerazioni rilevanti quando si implementa un server DTLS in MicroPython:
Il server dovrebbe passare un argomento aggiuntivo client_id quando chiama
SSLContext.wrap_socket(). Questo ID deve essere un oggettobytes(o simile) con un identificatore specifico del trasporto che rappresenti il client.L’approccio più semplice consiste nel convertire la tupla
(client_ip, client_port)restituita dasocket.recv_from()in una stringa di byte, cioè:_, 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())
La prima volta che un client si connette, la chiamata del server a
wrap_socketfallirà con un erroreOSError«Hello Verify Required». Questo accade perché il cookie DTLS «Hello Verify» non è ancora noto al client. Se lo stesso client si connette una seconda volta, allorawrap_socketavrà successo.I cookie DTLS per «Hello Verify» sono associati all’oggetto
SSLContext, quindi lo stesso oggettoSSLContextdovrebbe essere usato per incapsulare una connessione successiva dello stesso client. L’implementazione dei cookie include un timeout e ha un utilizzo costante della memoria indipendentemente da quanti client si connettono, quindi va bene riutilizzare lo stesso oggettoSSLContextper tutta la durata del server.
Costanti¶
- ssl.PROTOCOL_TLS_CLIENT: int¶
Valore supportato per il parametro protocol, che seleziona la modalità client TLS.
- ssl.PROTOCOL_TLS_SERVER: int¶
Valore supportato per il parametro protocol, che seleziona la modalità server TLS.
- ssl.PROTOCOL_DTLS_CLIENT: int¶
Valore supportato per il parametro protocol, che seleziona la modalità client DTLS.
- ssl.PROTOCOL_DTLS_SERVER: int¶
Valore supportato per il parametro protocol, che seleziona la modalità server DTLS.
- ssl.CERT_NONE: int¶
Valore supportato per il parametro cert_reqs e per l’attributo
SSLContext.verify_mode. Non viene eseguita alcuna verifica del certificato sul peer.
- ssl.CERT_OPTIONAL: int¶
Valore supportato per il parametro cert_reqs e per l’attributo
SSLContext.verify_mode. La verifica del certificato è opzionale. Nota che sulla OpenMV Cam questo si comporta comessl.CERT_NONE.
- ssl.CERT_REQUIRED: int¶
Valore supportato per il parametro cert_reqs e per l’attributo
SSLContext.verify_mode. È richiesto un certificato valido dal peer.