ssl — moduł SSL/TLS¶
Ten moduł zapewnia dostęp do mechanizmów szyfrowania Transport Layer Security (wcześniej i powszechnie znanego jako „Secure Sockets Layer”) oraz uwierzytelniania drugiej strony dla gniazd sieciowych, zarówno po stronie klienta, jak i serwera.
Wskazówka
Dopiero zaczynasz z TLS na kamerze? Zacznij od samouczka Praca z certyfikatami TLS. Przeprowadza on przez wybór typów kluczy, tworzenie i konwersję certyfikatów do formatu DER wymaganego przez kamerę, przeniesienie ich na urządzenie oraz weryfikację serwerów i klientów – wraz z kompletnymi, działającymi przykładami.
Informacja
MicroPython nie implementuje ssl.SSLError. Błędy SSL/TLS są zgłaszane jako OSError.
Przykłady¶
Klient TLS, weryfikujący certyfikat serwera względem certyfikatu CA (w formacie DER) zapisanego w systemie plików:
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()
W przypadku szybkiego, niezabezpieczonego połączenia (bez walidacji certyfikatu) można zamiast tego użyć funkcji pomocniczej ssl.wrap_socket()
ssock = ssl.wrap_socket(sock, server_hostname="example.com")
Serwer TLS, prezentujący własny certyfikat i klucz prywatny (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()
Funkcje¶
- 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¶
Opakowuje podane gniazdo sock i zwraca nowy obiekt opakowanego gniazda. Implementacja tej funkcji polega na utworzeniu najpierw obiektu
SSLContext, a następnie wywołaniu metodySSLContext.wrap_socket()na tym obiekcie kontekstu. Argumenty sock, server_side i server_hostname są przekazywane do wywołania metody bez zmian. Argument do_handshake jest przekazywany jako do_handshake_on_connect. Pozostałe argumenty mają następujące działanie:cert_reqs określa, czy druga strona (serwer lub klient) musi przedstawić prawidłowy certyfikat. Należy pamiętać, że
ssl.CERT_NONEissl.CERT_OPTIONALnie walidują żadnego certyfikatu; robi to tylkossl.CERT_REQUIRED.cadata to obiekt bytes zawierający łańcuch certyfikatów CA (w formacie DER), który posłuży do walidacji certyfikatu drugiej strony. Obecnie obsługiwany jest tylko pojedynczy certyfikat zakodowany w DER.
Klasy¶
- class ssl.SSLContext(protocol: int, /)¶
Tworzy nową instancję SSLContext. Argument protocol musi być jedną ze stałych
PROTOCOL_*.- load_cert_chain(certfile: str | bytes, keyfile: str | bytes) None¶
Wczytuje klucz prywatny i odpowiadający mu certyfikat. certfile to łańcuch znaków ze ścieżką pliku certyfikatu. keyfile to łańcuch znaków ze ścieżką pliku klucza prywatnego.
Różnica względem CPythona
Rozszerzenie MicroPython: certfile i keyfile mogą być obiektami bytes zamiast łańcuchów znaków, w którym to przypadku są interpretowane jako rzeczywiste dane certyfikatu/klucza.
- load_verify_locations(cafile: str | None = None, cadata: bytes | None = None) None¶
Wczytuje łańcuch certyfikatów CA, który posłuży do walidacji certyfikatu drugiej strony. cafile to ścieżka pliku certyfikatów CA. cadata to obiekt bytes zawierający certyfikaty CA. Należy podać tylko jeden z tych argumentów.
- set_ciphers(ciphers: List[str]) None¶
Ustawia dostępne szyfry dla gniazd tworzonych z tym kontekstem. ciphers powinno być listą łańcuchów znaków w formacie pakietów szyfrów 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¶
Przyjmuje strumień stream sock (zwykle instancję socket.socket typu
SOCK_STREAM) i zwraca instancję ssl.SSLSocket, opakowującą bazowy strumień. Zwrócony obiekt posiada zwykłe metody interfejsu stream, takie jakread(),write()itd.server_side wybiera, czy opakowane gniazdo znajduje się po stronie serwera, czy klienta. Gniazdo SSL po stronie serwera powinno być utworzone ze zwykłego gniazda zwróconego przez
accept()na nasłuchującym gnieździe serwera bez SSL.do_handshake_on_connect określa, czy uzgadnianie (handshake) jest wykonywane w ramach
wrap_socket, czy też jest odroczone do wykonania w ramach początkowych odczytów lub zapisów. W przypadku gniazd blokujących standardem jest natychmiastowe wykonanie uzgadniania. W przypadku gniazd nieblokujących (tj. gdy gniazdo sock przekazane dowrap_socketjest w trybie nieblokującym) uzgadnianie powinno być zwykle odroczone, ponieważ w przeciwnym raziewrap_socketblokuje aż do jego zakończenia.server_hostname jest przeznaczone do użytku jako klient i ustawia nazwę hosta do sprawdzenia względem otrzymanego certyfikatu serwera. Ustawia również nazwę dla Server Name Indication (SNI), pozwalając serwerowi przedstawić właściwy certyfikat.
client_id to argument rozszerzenia specyficzny dla MicroPython, używany tylko przy implementacji serwera DTLS. Szczegóły zob. Obsługa DTLS.
Ostrzeżenie
Domyślnie nie jest wykonywana żadna walidacja certyfikatu (
ssl.CERT_NONE). Aby uzyskać bezpieczne połączenie, należy zweryfikować certyfikat drugiej strony, ustawiając cert_reqs /SSLContext.verify_modenassl.CERT_REQUIRED; w przeciwnym razie połączenie jest podatne na ataki typu man-in-the-middle.wrap_socketw CPythonie zwraca obiektSSLSocket, który ma metody typowe dla gniazd, takie jaksend,recvitd.wrap_socketw MicroPython zwraca obiekt bardziej podobny doSSLObjectz CPythona, który nie posiada tych metod gniazda.
- verify_mode¶
Ustawia lub pobiera zachowanie weryfikacji certyfikatów drugiej strony. Musi być jedną ze stałych
CERT_*.Informacja
ssl.CERT_REQUIREDwymaga prawidłowego ustawienia daty/czasu urządzenia, np. za pomocą mpremote rtc --set lubntptime, a po stronie klienta musi być podana wartośćserver_hostname.
Obsługa DTLS¶
Różnica względem CPythona
To jest rozszerzenie MicroPython.
Ten moduł obsługuje DTLS w trybie klienta i serwera za pomocą stałych PROTOCOL_DTLS_CLIENT i PROTOCOL_DTLS_SERVER, których można użyć jako argumentu protocol klasy SSLContext.
W tym przypadku oczekuje się, że bazowe gniazdo zachowuje się jak gniazdo datagramowe (tj. jak gniazdo otwarte przez socket.socket z socket.AF_INET jako af i socket.SOCK_DGRAM jako type).
Obsługa serwera DTLS¶
Obsługa serwera DTLS w MicroPython jest skonfigurowana z „Hello Verify” zgodnie z wymaganiami DTLS 1.2. Jest to przezroczyste dla klientów DTLS, ale przy implementacji serwera DTLS w MicroPython należy uwzględnić pewne istotne kwestie:
Serwer powinien przekazać dodatkowy argument client_id przy wywoływaniu
SSLContext.wrap_socket(). Ten identyfikator musi być obiektembytes(lub podobnym) zawierającym specyficzny dla transportu identyfikator reprezentujący klienta.Najprostszym podejściem jest konwersja krotki
(client_ip, client_port)zwróconej przezsocket.recv_from()na łańcuch bajtów, 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())
Przy pierwszym połączeniu klienta wywołanie
wrap_socketpo stronie serwera zakończy się niepowodzeniem z błędemOSError„Hello Verify Required”. Dzieje się tak, ponieważ ciasteczko „Hello Verify” DTLS nie jest jeszcze znane klientowi. Jeśli ten sam klient połączy się po raz drugi,wrap_socketzakończy się powodzeniem.Ciasteczka DTLS dla „Hello Verify” są powiązane z obiektem
SSLContext, więc do opakowania kolejnego połączenia od tego samego klienta należy użyć tego samego obiektuSSLContext. Implementacja ciasteczek obejmuje limit czasu i ma stałe zużycie pamięci niezależnie od liczby łączących się klientów, więc można bezpiecznie używać tego samego obiektuSSLContextprzez cały okres działania serwera.
Stałe¶
- ssl.PROTOCOL_DTLS_CLIENT: int¶
Obsługiwana wartość parametru protocol, wybierająca tryb klienta DTLS.
- ssl.PROTOCOL_DTLS_SERVER: int¶
Obsługiwana wartość parametru protocol, wybierająca tryb serwera DTLS.
- ssl.CERT_NONE: int¶
Obsługiwana wartość parametru cert_reqs oraz atrybutu
SSLContext.verify_mode. Nie jest wykonywana żadna weryfikacja certyfikatu drugiej strony.
- ssl.CERT_OPTIONAL: int¶
Obsługiwana wartość parametru cert_reqs oraz atrybutu
SSLContext.verify_mode. Weryfikacja certyfikatu jest opcjonalna. Należy pamiętać, że na OpenMV Cam zachowuje się to jakssl.CERT_NONE.
- ssl.CERT_REQUIRED: int¶
Obsługiwana wartość parametru cert_reqs oraz atrybutu
SSLContext.verify_mode. Wymagany jest prawidłowy certyfikat od drugiej strony.