14.4.3. Самопідписані сертифікати¶
Самопідписаний сертифікат – найшвидший спосіб налаштувати TLS між двома пристроями, якими ви керуєте: обидва кінці довіряють єдиному сертифікату, який ви самі генеруєте. Він охоплює будь-яке розгортання, де ви налаштовуєте обидва боки з’єднання – публічний Центр сертифікації з’являється лише тоді, коли сторонні клієнти повинні підключатися без вказівки довіряти спеціальному сертифікату.
14.4.3.1. Створення самопідписаного сертифіката¶
Запустіть OpenSSL на вашій машині розробки. subjectAltName (SAN) – це те, що сучасні TLS-клієнти перевіряють під час верифікації імені хоста, тому встановіть його на ім’я (імена) хоста та/або IP-адресу (адреси), які клієнти використовуватимуть для з’єднання з камерою (лише CN є застарілим і ігнорується багатьма клієнтами). Замініть DNS:openmv / IP:192.168.1.50 на адресу, яку ваші клієнти фактично використовують для підключення.
ECDSA P-256 – рекомендовано:
# Generate a P-256 private key.
openssl ecparam -name prime256v1 -genkey -noout -out server.key
# Self-signed certificate valid for one year, with a SAN.
openssl req -new -x509 -key server.key -out server.crt -days 365 \
-subj "/CN=openmv" -addext "subjectAltName=DNS:openmv,IP:192.168.1.50"
ECDSA P-384 – надійніше, більший/повільніший:
openssl ecparam -name secp384r1 -genkey -noout -out server.key
openssl req -new -x509 -key server.key -out server.crt -days 365 \
-subj "/CN=openmv" -addext "subjectAltName=DNS:openmv,IP:192.168.1.50"
RSA-2048 – максимальна сумісність:
openssl req -new -x509 -newkey rsa:2048 -nodes -keyout server.key \
-out server.crt -days 365 -subj "/CN=openmv" \
-addext "subjectAltName=DNS:openmv,IP:192.168.1.50"
Примітка
Клієнтський сертифікат (що використовується для взаємної автентифікації, нижче) створюється точно тими самими командами – у самому сертифікаті немає нічого специфічного для клієнта. Просто згенеруйте другу незалежну пару ключ/сертифікат з іншими іменами (наприклад, client.key / client.crt) і використовуйте її на клієнті, як показано в прикладі mTLS. subjectAltName важливий лише для боку, чиє ім’я хоста перевіряє вузол (клієнт перевіряє ім’я сервера; ніхто не перевіряє ім’я клієнта), тому для клієнтського сертифіката його можна не вказувати. -subj / CN також є лише міткою для клієнтського сертифіката – серверна сторона тут перевіряє лише те, що сертифікат пов’язаний з довіреним CA, і ніколи не перевіряє ім’я – тому встановіть його на те, що ідентифікує цей клієнт (наприклад, /CN=sensor-01). Зберігайте будь-яке значення -subj, щоб OpenSSL міг генерувати сертифікат неінтерактивно.
Термін дії сертифіката встановлюється параметром -days; сертифікати мають кінцевий термін і повинні бути перегенеровані та перерозгорнуті до його закінчення.
14.4.3.2. Конвертування у формат DER¶
Конвертуйте як сертифікат, так і приватний ключ у формат DER перед копіюванням їх на камеру:
openssl x509 -in server.crt -outform DER -out server.der
openssl pkey -in server.key -outform DER -out server.key.der
14.4.3.3. Копіювання файлів на камеру¶
Скопіюйте файли DER у файлову систему камери – наприклад, перетягнувши їх на USB-накопичувач OpenMV Cam або за допомогою mpremote cp server.der : та mpremote cp server.key.der :. На боці верифікації також скопіюйте сертифікат CA / вузла у форматі DER.
Файли DER не обов’язково зберігати у файловій системі з можливістю запису. MicroPython також може монтувати образ ROMFS лише для читання за адресою /rom, а сертифікати, розміщені там, завантажуються як будь-який інший файл – наприклад, ctx.load_cert_chain("/rom/server.der", "/rom/server.key.der"). Образ ROMFS готується на вашій машині розробки і є незмінним під час виконання, тому сертифікат не може бути змінений на пристрої – це корисно для захисту виробничого пристрою. Зверніть увагу, що приватний ключ, збережений у ROMFS, все одно доступний для читання кодом, що виконується на камері; ROMFS захищає від модифікації, але не від витоку. Сертифікат у ROMFS можна замінити лише шляхом перезбірки та перепрошивки образу.
14.4.3.4. Використання сертифіката¶
Повний клієнт, який встановлює годинник, відкриває сокет, верифікує самопідписаний сервер і обмінюється даними:
import socket
import ssl
import ntptime
ntptime.settime() # correct clock for the validity check
# Open a plain TCP connection.
addr = socket.getaddrinfo("openmv", 8443)[0][-1]
sock = socket.socket()
sock.connect(addr)
# Wrap it for TLS, trusting the server's self-signed certificate.
ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
ctx.verify_mode = ssl.CERT_REQUIRED
ctx.load_verify_locations(cafile="server.der")
ssock = ctx.wrap_socket(sock, server_hostname="openmv")
ssock.write(b"hello\n")
print(ssock.read())
ssock.close()
Повний сервер, що представляє свій сертифікат і ключ:
import socket
import ssl
import ntptime
ntptime.settime() # correct clock for the validity check
ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
ctx.load_cert_chain("server.der", "server.key.der")
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)
while True:
client, addr = sock.accept()
sclient = ctx.wrap_socket(client, server_side=True)
sclient.write(b"hello\n")
print(sclient.read())
sclient.close()
Для взаємної автентифікації (mTLS) сервер додатково вимагає і верифікує клієнтський сертифікат, а клієнт надає свій власний:
# Server side: also demand and verify a client certificate.
ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
ctx.load_cert_chain("server.der", "server.key.der")
ctx.verify_mode = ssl.CERT_REQUIRED
ctx.load_verify_locations(cafile="client.der")
# Client side: present a certificate of our own.
ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
ctx.load_cert_chain("client.der", "client.key.der")
ctx.verify_mode = ssl.CERT_REQUIRED
ctx.load_verify_locations(cafile="server.der")
Повну документацію API дивіться в документації модуля ssl.
Примітка
Усе на цій сторінці незмінно застосовується до DTLS (TLS через UDP). Ключі, сертифікати, формат DER, модель довіри, проблеми з терміном дії та виклики load_cert_chain / load_verify_locations ідентичні; відрізняється лише транспорт – ви обгортаєте сокет socket.SOCK_DGRAM і вибираєте ssl.PROTOCOL_DTLS_CLIENT / ssl.PROTOCOL_DTLS_SERVER замість констант протоколу TLS. Єдина додаткова складність – це cookie для захисту від підробки на стороні сервера: перше з’єднання від нового клієнта очікувано завершується невдачею, і клієнт просто повторює спробу; подробиці дивіться в Підтримка DTLS.