ssl — Модуль SSL/TLS¶
Этот модуль предоставляет доступ к средствам шифрования Transport Layer Security (ранее и широко известного как «Secure Sockets Layer») и аутентификации участников для сетевых сокетов, как на стороне клиента, так и на стороне сервера.
Совет
Впервые работаете с TLS на камере? Начните с руководства Работа с сертификатами TLS. В нём разбирается выбор типов ключей, создание и преобразование сертификатов в формат DER, который требуется камере, перенос их на устройство, а также проверка серверов и клиентов – с полными рабочими примерами.
Примечание
MicroPython не реализует ssl.SSLError. Сбои SSL/TLS вместо этого вызываются как OSError.
Примеры¶
TLS-клиент, проверяющий сертификат сервера по сертификату CA (в формате DER), хранящемуся в файловой системе:
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()
Для быстрого, небезопасного соединения (без проверки сертификата) вместо этого можно использовать вспомогательную функцию ssl.wrap_socket():
ssock = ssl.wrap_socket(sock, server_hostname="example.com")
TLS-сервер, предъявляющий собственный сертификат и закрытый ключ (формат 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()
Функции¶
- 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¶
Оборачивает заданный sock и возвращает новый объект обёрнутого сокета. Реализация этой функции состоит в том, чтобы сначала создать
SSLContext, а затем вызвать методSSLContext.wrap_socket()для этого объекта контекста. Аргументы sock, server_side и server_hostname передаются в вызов метода без изменений. Аргумент do_handshake передаётся как do_handshake_on_connect. Остальные аргументы имеют следующее поведение:cert_reqs определяет, должен ли участник (сервер или клиент) предъявлять действительный сертификат. Обратите внимание, что
ssl.CERT_NONEиssl.CERT_OPTIONALне выполняют проверку какого-либо сертификата; это делает толькоssl.CERT_REQUIRED.cadata – это объект bytes, содержащий цепочку сертификатов CA (в формате DER), которая будет проверять сертификат участника. В настоящее время поддерживается только один сертификат в кодировке DER.
Классы¶
- class ssl.SSLContext(protocol: int, /)¶
Создаёт новый экземпляр SSLContext. Аргумент protocol должен быть одной из констант
PROTOCOL_*.- load_cert_chain(certfile: str | bytes, keyfile: str | bytes) None¶
Загружает закрытый ключ и соответствующий сертификат. certfile – это строка с путём к файлу сертификата. keyfile – это строка с путём к файлу закрытого ключа.
Отличие от CPython
Расширение MicroPython: certfile и keyfile могут быть объектами bytes вместо строк, в этом случае они интерпретируются как фактические данные сертификата/ключа.
- load_verify_locations(cafile: str | None = None, cadata: bytes | None = None) None¶
Загружает цепочку сертификатов CA, которая будет проверять сертификат участника. cafile – это путь к файлу сертификатов CA. cadata – это объект bytes, содержащий сертификаты CA. Следует указывать только один из этих аргументов.
- set_ciphers(ciphers: List[str]) None¶
Устанавливает доступные шифры для сокетов, создаваемых с этим контекстом. ciphers должен быть списком строк в формате наборов шифров 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¶
Принимает stream sock (обычно экземпляр socket.socket типа
SOCK_STREAM) и возвращает экземпляр ssl.SSLSocket, оборачивающий нижележащий поток. Возвращаемый объект имеет обычные методы интерфейса stream, такие какread(),write()и т. д.server_side выбирает, находится ли обёрнутый сокет на стороне сервера или клиента. SSL-сокет на стороне сервера должен создаваться из обычного сокета, возвращённого методом
accept()на не-SSL слушающем серверном сокете.do_handshake_on_connect определяет, выполняется ли рукопожатие как часть
wrap_socketили откладывается для выполнения в составе первых операций чтения или записи. Для блокирующих сокетов немедленное выполнение рукопожатия является стандартным. Для неблокирующих сокетов (т. е. когда переданный вwrap_socketsock находится в неблокирующем режиме) рукопожатие обычно следует откладывать, поскольку иначеwrap_socketблокируется до его завершения.server_hostname используется на стороне клиента и задаёт имя хоста для проверки по полученному сертификату сервера. Оно также задаёт имя для Server Name Indication (SNI), позволяя серверу предъявить правильный сертификат.
client_id – это специфичный для MicroPython аргумент-расширение, используемый только при реализации DTLS-сервера. Подробности см. в Поддержка DTLS.
Предупреждение
По умолчанию проверка сертификата не выполняется (
ssl.CERT_NONE). Для безопасного соединения необходимо проверять сертификат участника, установив cert_reqs /SSLContext.verify_modeвssl.CERT_REQUIRED; иначе соединение уязвимо для атак типа «человек посередине».Функция CPython
wrap_socketвозвращает объектSSLSocket, имеющий типичные для сокетов методы, такие какsend,recvи т. д. Функция MicroPythonwrap_socketвозвращает объект, более похожий наSSLObjectиз CPython, у которого этих методов сокета нет.
- verify_mode¶
Устанавливает или получает поведение при проверке сертификатов участников. Должно быть одной из констант
CERT_*.Примечание
ssl.CERT_REQUIREDтребует правильной установки даты/времени устройства, например с помощью mpremote rtc --set илиntptime, аserver_hostnameдолжен быть указан на стороне клиента.
Поддержка DTLS¶
Отличие от CPython
Это расширение MicroPython.
Этот модуль поддерживает DTLS в режиме клиента и сервера через константы PROTOCOL_DTLS_CLIENT и PROTOCOL_DTLS_SERVER, которые можно использовать как аргумент protocol для SSLContext.
В этом случае ожидается, что нижележащий сокет ведёт себя как датаграммный сокет (т. е. как сокет, открытый с помощью socket.socket с socket.AF_INET в качестве af и socket.SOCK_DGRAM в качестве type).
Поддержка DTLS-сервера¶
Поддержка DTLS-сервера в MicroPython настроена с «Hello Verify», как это требуется для DTLS 1.2. Для DTLS-клиентов это прозрачно, но при реализации DTLS-сервера в MicroPython есть важные соображения:
Сервер должен передавать дополнительный аргумент client_id при вызове
SSLContext.wrap_socket(). Этот идентификатор должен быть объектомbytes(или подобным) со специфичным для транспорта идентификатором, представляющим клиента.Простейший подход – преобразовать кортеж
(client_ip, client_port), возвращённый изsocket.recv_from(), в строку байтов, т. е.:_, 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())
При первом подключении клиента вызов сервером
wrap_socketзавершится ошибкойOSError«Hello Verify Required». Это происходит потому, что DTLS-cookie «Hello Verify» ещё не известен клиенту. Если тот же клиент подключится во второй раз, тоwrap_socketзавершится успешно.DTLS-cookie для «Hello Verify» связаны с объектом
SSLContext, поэтому для оборачивания последующего соединения от того же клиента следует использовать тот же объектSSLContext. Реализация cookie включает тайм-аут и использует постоянный объём памяти независимо от того, сколько клиентов подключается, поэтому допустимо повторно использовать один и тот же объектSSLContextна протяжении всего времени работы сервера.
Константы¶
- ssl.PROTOCOL_TLS_CLIENT: int¶
Поддерживаемое значение параметра protocol, выбирающее режим TLS-клиента.
- ssl.PROTOCOL_TLS_SERVER: int¶
Поддерживаемое значение параметра protocol, выбирающее режим TLS-сервера.
- ssl.PROTOCOL_DTLS_CLIENT: int¶
Поддерживаемое значение параметра protocol, выбирающее режим DTLS-клиента.
- ssl.PROTOCOL_DTLS_SERVER: int¶
Поддерживаемое значение параметра protocol, выбирающее режим DTLS-сервера.
- ssl.CERT_NONE: int¶
Поддерживаемое значение параметра cert_reqs и атрибута
SSLContext.verify_mode. Проверка сертификата участника не выполняется.
- ssl.CERT_OPTIONAL: int¶
Поддерживаемое значение параметра cert_reqs и атрибута
SSLContext.verify_mode. Проверка сертификата необязательна. Обратите внимание, что на OpenMV Cam это ведёт себя какssl.CERT_NONE.
- ssl.CERT_REQUIRED: int¶
Поддерживаемое значение параметра cert_reqs и атрибута
SSLContext.verify_mode. От участника требуется действительный сертификат.