14.4.3. Självsignerade certifikat

Ett självsignerat certifikat är det snabbaste sättet att få TLS att fungera mellan två enheter du kontrollerar: båda ändarna litar på ett enda certifikat som du genererar själv. Det täcker varje driftsättning där du konfigurerar båda sidorna av anslutningen – en offentlig Certificate Authority kommer in i bilden först när tredjepartsklienter måste ansluta utan att bli tillsagda att lita på ett anpassat certifikat.

14.4.3.1. Skapa ett självsignerat certifikat

Kör OpenSSL på din utvecklingsmaskin. subjectAltName (SAN) är vad moderna TLS-klienter kontrollerar under värdnamnsverifieringen, så sätt det till de värdnamn och/eller IP-adresser som klienterna kommer att använda för att nå kameran (CN ensamt är föråldrat och ignoreras av många klienter). Ersätt DNS:openmv / IP:192.168.1.50 med adressen som dina klienter faktiskt ansluter till.

ECDSA P-256 – rekommenderas:

# 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 – starkare, större/långsammare:

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 – maximal kompatibilitet:

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"

Anteckning

Ett klient-certifikat (som används för ömsesidig autentisering, nedan) skapas med exakt samma kommandon – det finns inget klientspecifikt med själva certifikatet. Generera bara ett andra, oberoende nyckel-/certifikatpar under andra namn (t.ex. client.key / client.crt) och använd det på klienten som visas i mTLS-exemplet. subjectAltName spelar bara roll för den sida vars värdnamn motparten verifierar (klienten kontrollerar serverns namn; ingenting kontrollerar klientens), så det kan utelämnas för ett certifikat enbart för klienter. -subj / CN är på samma sätt bara en etikett på ett klientcertifikat – serversidan här kontrollerar bara att certifikatet kedjar till en betrodd CA, den matchar aldrig namnet – så sätt det till vad som än identifierar den klienten (t.ex. /CN=sensor-01). Behåll något -subj-värde oavsett, så att OpenSSL kan generera certifikatet icke-interaktivt.

Certifikatets livslängd ställs in med -days; certifikat går ut och måste regenereras och driftsättas på nytt innan dess.

14.4.3.2. Konvertera till DER

Konvertera både certifikatet och den privata nyckeln till DER innan du kopierar dem till kameran:

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. Kopiera filer till kameran

Kopiera DER-filerna till kamerans filsystem – till exempel genom att dra dem till OpenMV Cam:ens USB-enhet, eller med mpremote cp server.der : och mpremote cp server.key.der :. På den verifierande sidan, kopiera även CA-/motpartscertifikatet i DER-form.

DER-filerna behöver inte finnas på det skrivbara filsystemet. MicroPython kan även montera en skrivskyddad ROMFS-avbildning på /rom, och certifikat som placerats där laddas precis som vilken annan fil som helst – t.ex. ctx.load_cert_chain("/rom/server.der", "/rom/server.key.der"). En ROMFS-avbildning förbereds på din utvecklingsmaskin och är skrivskyddad vid körning, så certifikatet kan inte ändras på enheten – användbart för att låsa ner en produktionsenhet. Observera att en privat nyckel som lagras i ROMFS fortfarande är läsbar av kod som körs på kameran; ROMFS skyddar mot modifiering, inte extrahering. Ett ROMFS-residerande certifikat kan endast bytas ut genom att bygga om och flasha om avbildningen.

14.4.3.4. Använda certifikatet

En fullständig klient som ställer klockan, öppnar en socket, verifierar en självsignerad server och utbyter data:

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()

En fullständig server som presenterar sitt certifikat och sin nyckel:

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()

För ömsesidig autentisering (mTLS) kräver och verifierar servern dessutom ett klientcertifikat, och klienten presenterar ett eget:

# 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")

Se dokumentationen för modulen ssl för det fullständiga API:et.

Anteckning

Allt på den här sidan gäller oförändrat för DTLS (TLS över UDP). Nycklarna, certifikaten, DER-formatet, förtroendemodellen, utgångsfrågorna och anropen load_cert_chain / load_verify_locations är identiska; bara transporten skiljer sig – du omsluter en socket.SOCK_DGRAM-socket och väljer ssl.PROTOCOL_DTLS_CLIENT / ssl.PROTOCOL_DTLS_SERVER istället för TLS-protokollkonstanterna. Den enda extra komplikationen är en anti-spoofing-cookie på serversidan – den första anslutningen från en ny klient förväntas misslyckas och klienten gör helt enkelt ett nytt försök; se DTLS-stöd för detaljer.