ssl — Módulo SSL/TLS¶
Este módulo proporciona acceso a las funciones de cifrado y autenticación de pares de Transport Layer Security (anteriormente conocido ampliamente como «Secure Sockets Layer») para sockets de red, tanto del lado del cliente como del servidor.
Truco
¿Es nuevo en TLS en la cámara? Comience con el tutorial Trabajar con certificados TLS. Le guía a través de la elección de tipos de clave, la creación y conversión de certificados al formato DER que requiere la cámara, su transferencia al dispositivo y la verificación de servidores y clientes, con ejemplos completos y funcionales.
Nota
MicroPython no implementa ssl.SSLError. Los fallos de SSL/TLS se generan como OSError en su lugar.
Ejemplos¶
Cliente TLS que verifica el certificado del servidor frente a un certificado de CA (en formato DER) almacenado en el sistema de archivos:
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()
Para una conexión rápida y no segura (sin validación de certificados) se puede usar en su lugar la función de conveniencia ssl.wrap_socket():
ssock = ssl.wrap_socket(sock, server_hostname="example.com")
Servidor TLS que presenta su propio certificado y clave privada (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()
Funciones¶
- 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¶
Envuelve el sock indicado y devuelve un nuevo objeto socket envuelto. La implementación de esta función consiste en crear primero un
SSLContexty luego llamar al métodoSSLContext.wrap_socket()sobre ese objeto de contexto. Los argumentos sock, server_side y server_hostname se pasan sin cambios a la llamada al método. El argumento do_handshake se pasa como do_handshake_on_connect. Los argumentos restantes tienen el siguiente comportamiento:cert_reqs determina si el par (servidor o cliente) debe presentar un certificado válido. Tenga en cuenta que
ssl.CERT_NONEyssl.CERT_OPTIONALno validan ningún certificado; solossl.CERT_REQUIREDlo hace.cadata es un objeto bytes que contiene la cadena de certificados de CA (en formato DER) que validará el certificado del par. Actualmente solo se admite un único certificado codificado en DER.
Clases¶
- class ssl.SSLContext(protocol: int, /)¶
Crea una nueva instancia de SSLContext. El argumento protocol debe ser una de las constantes
PROTOCOL_*.- load_cert_chain(certfile: str | bytes, keyfile: str | bytes) None¶
Carga una clave privada y el certificado correspondiente. certfile es una cadena con la ruta del archivo del certificado. keyfile es una cadena con la ruta del archivo de la clave privada.
Diferencia con CPython
Extensión de MicroPython: certfile y keyfile pueden ser objetos bytes en lugar de cadenas, en cuyo caso se interpretan como los datos reales del certificado/clave.
- load_verify_locations(cafile: str | None = None, cadata: bytes | None = None) None¶
Carga la cadena de certificados de CA que validará el certificado del par. cafile es la ruta del archivo de los certificados de CA. cadata es un objeto bytes que contiene los certificados de CA. Solo se debe proporcionar uno de estos argumentos.
- get_ciphers() List[str]¶
Obtiene una lista de los cifrados habilitados, devuelta como una lista de cadenas.
- set_ciphers(ciphers: List[str]) None¶
Establece los cifrados disponibles para los sockets creados con este contexto. ciphers debe ser una lista de cadenas en el formato de conjunto de cifrados de la 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¶
Toma un sock de tipo stream (normalmente una instancia de socket.socket de tipo
SOCK_STREAM) y devuelve una instancia de ssl.SSLSocket que envuelve el flujo subyacente. El objeto devuelto tiene los métodos habituales de la interfaz stream, comoread(),write(), etc.server_side selecciona si el socket envuelto está del lado del servidor o del cliente. Un socket SSL del lado del servidor debe crearse a partir de un socket normal devuelto por
accept()sobre un socket de escucha no SSL.do_handshake_on_connect determina si el handshake se realiza como parte de
wrap_socketo si se aplaza para realizarse como parte de las primeras lecturas o escrituras. Para sockets bloqueantes, realizar el handshake inmediatamente es lo habitual. Para sockets no bloqueantes (es decir, cuando el sock pasado awrap_socketestá en modo no bloqueante), el handshake generalmente debe aplazarse, ya que de lo contrariowrap_socketse bloquea hasta que se completa.server_hostname es para usarse como cliente, y establece el nombre de host a comprobar frente al certificado del servidor recibido. También establece el nombre para la Server Name Indication (SNI), lo que permite al servidor presentar el certificado adecuado.
client_id es un argumento de extensión específico de MicroPython utilizado únicamente al implementar un servidor DTLS. Consulte Compatibilidad con DTLS para más detalles.
Advertencia
De forma predeterminada no se realiza ninguna validación de certificados (
ssl.CERT_NONE). Para una conexión segura debe verificar el certificado del par estableciendo cert_reqs /SSLContext.verify_modeenssl.CERT_REQUIRED; de lo contrario, la conexión es vulnerable a ataques de intermediario (man-in-the-middle).El
wrap_socketde CPython devuelve un objetoSSLSocketque tiene métodos típicos de los sockets, comosend,recv, etc. Elwrap_socketde MicroPython devuelve un objeto más similar alSSLObjectde CPython, que no tiene estos métodos de socket.
- verify_mode¶
Establece u obtiene el comportamiento para la verificación de los certificados de los pares. Debe ser una de las constantes
CERT_*.Nota
ssl.CERT_REQUIREDrequiere que la fecha/hora del dispositivo esté correctamente configurada, por ejemplo usando mpremote rtc --set ontptime, y debe especificarseserver_hostnamecuando se está del lado del cliente.
Compatibilidad con DTLS¶
Diferencia con CPython
Esta es una extensión de MicroPython.
Este módulo admite DTLS en modo cliente y servidor mediante las constantes PROTOCOL_DTLS_CLIENT y PROTOCOL_DTLS_SERVER, que pueden usarse como el argumento protocol de SSLContext.
En este caso se espera que el socket subyacente se comporte como un socket de datagramas (es decir, como el socket abierto con socket.socket usando socket.AF_INET como af y socket.SOCK_DGRAM como type).
Compatibilidad con servidor DTLS¶
La compatibilidad con servidor DTLS de MicroPython está configurada con «Hello Verify» según lo requerido por DTLS 1.2. Esto es transparente para los clientes DTLS, pero hay consideraciones relevantes al implementar un servidor DTLS en MicroPython:
El servidor debe pasar un argumento adicional client_id al llamar a
SSLContext.wrap_socket(). Este ID debe ser un objetobytes(o similar) con un identificador específico del transporte que represente al cliente.El enfoque más sencillo es convertir la tupla
(client_ip, client_port)devuelta porsocket.recv_from()en una cadena de bytes, es decir:_, 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 primera vez que un cliente se conecta, la llamada del servidor a
wrap_socketfallará con un errorOSError«Hello Verify Required». Esto se debe a que la cookie «Hello Verify» de DTLS aún no es conocida por el cliente. Si el mismo cliente se conecta una segunda vez, entonceswrap_sockettendrá éxito.Las cookies DTLS para «Hello Verify» están asociadas al objeto
SSLContext, por lo que se debe usar el mismo objetoSSLContextpara envolver una conexión posterior del mismo cliente. La implementación de cookies incluye un tiempo de espera y tiene un uso de memoria constante independientemente de cuántos clientes se conecten, por lo que es correcto reutilizar el mismo objetoSSLContextdurante toda la vida del servidor.
Constantes¶
- ssl.PROTOCOL_TLS_CLIENT: int¶
Valor admitido para el parámetro protocol, que selecciona el modo cliente TLS.
- ssl.PROTOCOL_TLS_SERVER: int¶
Valor admitido para el parámetro protocol, que selecciona el modo servidor TLS.
- ssl.PROTOCOL_DTLS_CLIENT: int¶
Valor admitido para el parámetro protocol, que selecciona el modo cliente DTLS.
- ssl.PROTOCOL_DTLS_SERVER: int¶
Valor admitido para el parámetro protocol, que selecciona el modo servidor DTLS.
- ssl.CERT_NONE: int¶
Valor admitido para el parámetro cert_reqs y para el atributo
SSLContext.verify_mode. No se realiza ninguna verificación de certificado sobre el par.
- ssl.CERT_OPTIONAL: int¶
Valor admitido para el parámetro cert_reqs y para el atributo
SSLContext.verify_mode. La verificación del certificado es opcional. Tenga en cuenta que en la OpenMV Cam esto se comporta comossl.CERT_NONE.
- ssl.CERT_REQUIRED: int¶
Valor admitido para el parámetro cert_reqs y para el atributo
SSLContext.verify_mode. Se requiere un certificado válido del par.