14.4.3. Certificati autofirmati¶
Un certificato autofirmato è il modo più rapido per far funzionare il TLS tra due dispositivi che controlli: entrambe le estremità si fidano di un singolo certificato che generi tu stesso. Copre ogni distribuzione in cui configuri entrambi i lati della connessione – una Certificate Authority pubblica entra in gioco solo quando client di terze parti devono connettersi senza che venga loro indicato di fidarsi di un certificato personalizzato.
14.4.3.1. Creare un certificato autofirmato¶
Esegui OpenSSL sulla tua macchina di sviluppo. Il subjectAltName (SAN) è ciò che i client TLS moderni controllano durante la verifica del nome host, quindi impostalo sul nome host (o sui nomi host) e/o sull’indirizzo IP (o sugli indirizzi IP) che i client useranno per raggiungere la camera (il solo CN è obsoleto ed è ignorato da molti client). Sostituisci DNS:openmv / IP:192.168.1.50 con l’indirizzo a cui i tuoi client si connettono effettivamente.
ECDSA P-256 – consigliato:
# 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 – più robusto, più grande/più lento:
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 – massima compatibilità:
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"
Nota
Un certificato client (usato per l’autenticazione reciproca, più avanti) si crea esattamente con questi stessi comandi – non c’è nulla di specifico del client nel certificato stesso. Genera semplicemente una seconda coppia chiave/certificato indipendente con nomi diversi (ad esempio client.key / client.crt) e usala sul client come mostrato nell’esempio mTLS. Il subjectAltName conta solo per il lato il cui nome host viene verificato dal peer (il client controlla il nome del server; nulla controlla quello del client), quindi può essere omesso per un certificato solo-client. Il -subj / CN è allo stesso modo solo un’etichetta su un certificato client – qui il lato server controlla solo che il certificato si concateni a una CA attendibile, non confronta mai il nome – quindi impostalo su qualsiasi cosa identifichi quel client (ad esempio /CN=sensor-01). Mantieni comunque un qualche valore -subj, così che OpenSSL possa generare il certificato in modo non interattivo.
La durata del certificato è impostata da -days; i certificati scadono e devono essere rigenerati e ridistribuiti prima di allora.
14.4.3.2. Conversione in DER¶
Converti sia il certificato sia la chiave privata in DER prima di copiarli sulla camera:
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. Copiare i file sulla camera¶
Copia i file DER sul filesystem della camera – ad esempio trascinandoli sull’unità USB dell’OpenMV Cam, oppure con mpremote cp server.der : e mpremote cp server.key.der :. Sul lato che effettua la verifica, copia anche il certificato CA / del peer in formato DER.
I file DER non devono necessariamente risiedere sul filesystem scrivibile. MicroPython può anche montare un’immagine ROMFS di sola lettura in /rom e i certificati lì posizionati vengono caricati esattamente come qualsiasi altro file – ad esempio ctx.load_cert_chain("/rom/server.der", "/rom/server.key.der"). Un’immagine ROMFS viene preparata sulla tua macchina di sviluppo ed è di sola lettura in fase di esecuzione, quindi il certificato non può essere alterato sul dispositivo – utile per blindare un’unità di produzione. Nota che una chiave privata memorizzata in ROMFS è comunque leggibile dal codice in esecuzione sulla camera; ROMFS protegge dalla modifica, non dall”estrazione. Un certificato residente in ROMFS può essere sostituito solo ricompilando e riprogrammando l’immagine.
14.4.3.4. Usare il certificato¶
Un client completo che imposta l’orologio, apre un socket, verifica un server autofirmato e scambia dati:
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()
Un server completo che presenta il proprio certificato e la propria chiave:
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()
Per l”autenticazione reciproca (mTLS) il server richiede e verifica inoltre un certificato client, e il client ne presenta uno proprio:
# 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")
Consulta la documentazione del modulo ssl per l’API completa.
Nota
Tutto quanto presente in questa pagina si applica invariato al DTLS (TLS su UDP). Le chiavi, i certificati, il formato DER, il modello di fiducia, le questioni di scadenza e le chiamate load_cert_chain / load_verify_locations sono identiche; differisce solo il trasporto – si incapsula un socket socket.SOCK_DGRAM e si seleziona ssl.PROTOCOL_DTLS_CLIENT / ssl.PROTOCOL_DTLS_SERVER invece delle costanti del protocollo TLS. L’unica complicazione aggiuntiva è un cookie anti-spoofing lato server – la prima connessione da un nuovo client è prevista fallire e il client semplicemente riprova; vedi Supporto DTLS per i dettagli.