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 — це об’єкт байтів, що містить ланцюжок сертифікатів 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 можуть бути об’єктами байтів замість рядків, у такому випадку вони інтерпретуються як безпосередньо дані сертифіката/ключа.
- load_verify_locations(cafile: str | None = None, cadata: bytes | None = None) None¶
Завантажує ланцюжок сертифікатів CA для перевірки сертифіката однорангового вузла. cafile — шлях до файлу з сертифікатами CA. cadata — об’єкт байтів, що містить сертифікати CA. Слід вказати лише один із цих аргументів.
- set_ciphers(ciphers: List[str]) None¶
Встановлює доступні шифри для сокетів, створених з цим контекстом. ciphers має бути списком рядків у форматі IANA cipher suite.
- 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, або воно відкладається до перших читань чи записів. Для блокувальних сокетів негайне виконання рукостискання є стандартним. Для неблокувальних сокетів (тобто коли sock, переданий уwrap_socket, перебуває в неблокувальному режимі) рукостискання зазвичай слід відкласти, інакшеwrap_socketблокуватиметься до його завершення.server_hostname використовується на стороні клієнта і встановлює ім’я хоста для перевірки отриманого сертифіката сервера. Також задає ім’я для Server Name Indication (SNI), що дозволяє серверу надати відповідний сертифікат.
client_id — це специфічний для MicroPython аргумент-розширення, що використовується лише при реалізації DTLS-сервера. Детальніше дивіться Підтримка DTLS.
Попередження
За замовчуванням перевірка сертифіката не виконується (
ssl.CERT_NONE). Для безпечного з’єднання необхідно перевіряти сертифікат однорангового вузла, встановивши cert_reqs /SSLContext.verify_modeвssl.CERT_REQUIRED; інакше з’єднання вразливе до атак типу «людина посередині».wrap_socketу CPython повертає об’єктSSLSocketз методами, характерними для сокетів, такими якsend,recvтощо.wrap_socketу MicroPython повертає об’єкт, більш схожий на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 «Hello Verify» ще не відома клієнту. Якщо той самий клієнт підключиться вдруге,wrap_socketзавершиться успішно.Куки DTLS для «Hello Verify» пов’язані з об’єктом
SSLContext, тому для обгортання наступного з’єднання від того самого клієнта слід використовувати той самий об’єктSSLContext. Реалізація куків включає таймаут і використовує постійну пам’ять незалежно від кількості клієнтів, що підключаються, тому можна повторно використовувати той самий об’єкт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. Від однорангового вузла вимагається дійсний сертифікат.