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 SSLContext puis à appeler la méthode SSLContext.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_NONE et ssl.CERT_OPTIONAL ne valident aucun certificat ; seul ssl.CERT_REQUIRED le 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 que read(), 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_socket ou 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_socket est en mode non bloquant), la poignée de main doit généralement être différée car sinon wrap_socket bloque 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_mode sur ssl.CERT_REQUIRED ; sinon la connexion est vulnérable aux attaques de l’homme du milieu.

Le wrap_socket de CPython renvoie un objet SSLSocket qui possède des méthodes typiques des sockets, telles que send, recv, etc. Le wrap_socket de MicroPython renvoie un objet plus proche de l”SSLObject de 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_REQUIRED exige que la date/heure de l’appareil soit correctement définie, par exemple à l’aide de mpremote rtc --set ou de ntptime, et server_hostname doit ê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 objet bytes (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é par socket.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 erreur OSError « 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_socket réussit.

  • Les cookies DTLS pour « Hello Verify » sont associés à l’objet SSLContext, donc le même objet SSLContext doit ê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 objet SSLContext pendant 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 comme ssl.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.