ssl — Module SSL/TLS¶
Ce module donne accès aux fonctionnalités de chiffrement et d’authentification des pairs Transport Layer Security (précédemment et largement connu sous le nom de « Secure Sockets Layer ») pour les sockets réseau, à la fois côté client et côté serveur.
Astuce
Vous découvrez TLS sur la caméra ? Commencez par le tutoriel Travailler avec les certificats TLS. Il explique comment choisir les types de clés, créer et convertir des certificats au format DER requis par la caméra, les transférer sur l’appareil, et vérifier les serveurs et les clients – avec des exemples complets et fonctionnels.
Note
MicroPython n’implémente pas ssl.SSLError. Les échecs SSL/TLS sont levés sous forme d”OSError à la place.
Exemples¶
Client TLS, vérifiant le certificat du serveur par rapport à un certificat d’autorité de certification (au format DER) stocké sur le système de fichiers
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()
Pour une connexion rapide et non sécurisée (sans validation de certificat), la fonction de commodité ssl.wrap_socket() peut être utilisée à la place
ssock = ssl.wrap_socket(sock, server_hostname="example.com")
Serveur TLS, présentant son propre certificat et sa clé privée (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()
Fonctions¶
- 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¶
Enveloppe le sock donné et renvoie un nouvel objet socket enveloppé. L’implémentation de cette fonction consiste d’abord à créer un
SSLContextpuis à appeler la méthodeSSLContext.wrap_socket()sur cet objet contexte. Les arguments sock, server_side et server_hostname sont transmis tels quels à l’appel de méthode. L’argument do_handshake est transmis en tant que do_handshake_on_connect. Les arguments restants ont le comportement suivant :cert_reqs détermine si le pair (serveur ou client) doit présenter un certificat valide. Notez que
ssl.CERT_NONEetssl.CERT_OPTIONALne valident aucun certificat ; seulssl.CERT_REQUIREDle fait.cadata est un objet bytes contenant la chaîne de certificats d’autorité de certification (au format DER) qui validera le certificat du pair. Actuellement, un seul certificat encodé en DER est pris en charge.
Classes¶
- class ssl.SSLContext(protocol: int, /)¶
Crée une nouvelle instance de SSLContext. L’argument protocol doit être l’une des constantes
PROTOCOL_*.- load_cert_chain(certfile: str | bytes, keyfile: str | bytes) None¶
Charge une clé privée et le certificat correspondant. certfile est une chaîne contenant le chemin du fichier du certificat. keyfile est une chaîne contenant le chemin du fichier de la clé privée.
Différence avec CPython
Extension MicroPython : certfile et keyfile peuvent être des objets bytes au lieu de chaînes, auquel cas ils sont interprétés comme les données réelles du certificat/de la clé.
- load_verify_locations(cafile: str | None = None, cadata: bytes | None = None) None¶
Charge la chaîne de certificats d’autorité de certification qui validera le certificat du pair. cafile est le chemin du fichier des certificats d’autorité de certification. cadata est un objet bytes contenant les certificats d’autorité de certification. Un seul de ces arguments doit être fourni.
- get_ciphers() List[str]¶
Obtient la liste des suites de chiffrement activées, renvoyée sous forme de liste de chaînes.
- set_ciphers(ciphers: List[str]) None¶
Définit les suites de chiffrement disponibles pour les sockets créés avec ce contexte. ciphers doit être une liste de chaînes au format de suite de chiffrement 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¶
Prend un stream sock (généralement une instance socket.socket de type
SOCK_STREAM) et renvoie une instance de ssl.SSLSocket, enveloppant le flux sous-jacent. L’objet renvoyé possède les méthodes habituelles de l’interface stream telles queread(),write(), etc.server_side détermine si le socket enveloppé se trouve côté serveur ou côté client. Un socket SSL côté serveur doit être créé à partir d’un socket normal renvoyé par
accept()sur un socket serveur d’écoute non-SSL.do_handshake_on_connect détermine si la poignée de main est effectuée dans le cadre de
wrap_socketou si elle est différée pour être effectuée dans le cadre des premières lectures ou écritures. Pour les sockets bloquants, effectuer la poignée de main immédiatement est la norme. Pour les sockets non bloquants (c’est-à-dire lorsque le sock transmis àwrap_socketest en mode non bloquant), la poignée de main doit généralement être différée car sinonwrap_socketbloque jusqu’à son achèvement.server_hostname est destiné à une utilisation côté client et définit le nom d’hôte à vérifier par rapport au certificat de serveur reçu. Il définit également le nom pour l’indication du nom de serveur (SNI), permettant au serveur de présenter le certificat approprié.
client_id est un argument d’extension spécifique à MicroPython utilisé uniquement lors de l’implémentation d’un serveur DTLS. Voir Prise en charge DTLS pour plus de détails.
Avertissement
Par défaut, aucune validation de certificat n’est effectuée (
ssl.CERT_NONE). Pour une connexion sécurisée, vous devez vérifier le certificat du pair en définissant cert_reqs /SSLContext.verify_modesurssl.CERT_REQUIRED; sinon la connexion est vulnérable aux attaques de l’homme du milieu.Le
wrap_socketde CPython renvoie un objetSSLSocketqui possède des méthodes typiques des sockets, telles quesend,recv, etc. Lewrap_socketde MicroPython renvoie un objet plus proche de l”SSLObjectde CPython, qui ne possède pas ces méthodes de socket.
- verify_mode¶
Définit ou obtient le comportement de vérification des certificats des pairs. Doit être l’une des constantes
CERT_*.Note
ssl.CERT_REQUIREDexige que la date/heure de l’appareil soit correctement définie, par exemple à l’aide de mpremote rtc --set ou dentptime, etserver_hostnamedoit être spécifié côté client.
Prise en charge DTLS¶
Différence avec CPython
Il s’agit d’une extension MicroPython.
Ce module prend en charge DTLS en mode client et serveur via les constantes PROTOCOL_DTLS_CLIENT et PROTOCOL_DTLS_SERVER qui peuvent être utilisées comme argument protocol de SSLContext.
Dans ce cas, le socket sous-jacent est censé se comporter comme un socket datagramme (c’est-à-dire comme le socket ouvert avec socket.socket avec socket.AF_INET comme af et socket.SOCK_DGRAM comme type).
Prise en charge du serveur DTLS¶
La prise en charge du serveur DTLS de MicroPython est configurée avec « Hello Verify » comme requis pour DTLS 1.2. Ceci est transparent pour les clients DTLS, mais il y a des considérations importantes à prendre en compte lors de l’implémentation d’un serveur DTLS dans MicroPython :
Le serveur doit transmettre un argument supplémentaire client_id lors de l’appel à
SSLContext.wrap_socket(). Cet identifiant doit être un objetbytes(ou similaire) avec un identifiant spécifique au transport représentant le client.L’approche la plus simple consiste à convertir le tuple
(client_ip, client_port)renvoyé parsocket.recv_from()en une chaîne d’octets, c’est-à-dire_, 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 première fois qu’un client se connecte, l’appel du serveur à
wrap_socketéchoue avec une erreurOSError« Hello Verify Required ». Ceci est dû au fait que le cookie « Hello Verify » de DTLS n’est pas encore connu du client. Si le même client se connecte une seconde fois,wrap_socketréussit.Les cookies DTLS pour « Hello Verify » sont associés à l’objet
SSLContext, donc le même objetSSLContextdoit être utilisé pour envelopper une connexion ultérieure du même client. L’implémentation des cookies inclut un délai d’expiration et a une utilisation constante de la mémoire quel que soit le nombre de clients qui se connectent, il est donc acceptable de réutiliser le même objetSSLContextpendant toute la durée de vie du serveur.
Constantes¶
- ssl.PROTOCOL_TLS_CLIENT: int¶
Valeur prise en charge pour le paramètre protocol, sélectionnant le mode client TLS.
- ssl.PROTOCOL_TLS_SERVER: int¶
Valeur prise en charge pour le paramètre protocol, sélectionnant le mode serveur TLS.
- ssl.PROTOCOL_DTLS_CLIENT: int¶
Valeur prise en charge pour le paramètre protocol, sélectionnant le mode client DTLS.
- ssl.PROTOCOL_DTLS_SERVER: int¶
Valeur prise en charge pour le paramètre protocol, sélectionnant le mode serveur DTLS.
- ssl.CERT_NONE: int¶
Valeur prise en charge pour le paramètre cert_reqs et l’attribut
SSLContext.verify_mode. Aucune vérification de certificat n’est effectuée sur le pair.
- ssl.CERT_OPTIONAL: int¶
Valeur prise en charge pour le paramètre cert_reqs et l’attribut
SSLContext.verify_mode. La vérification du certificat est facultative. Notez que sur l’OpenMV Cam, cela se comporte commessl.CERT_NONE.
- ssl.CERT_REQUIRED: int¶
Valeur prise en charge pour le paramètre cert_reqs et l’attribut
SSLContext.verify_mode. Un certificat valide est requis de la part du pair.