14.4.3. Certificados autoassinados¶
Um certificado autoassinado é a forma mais rápida de fazer TLS funcionar entre dois dispositivos que controla: ambos os extremos confiam num único certificado que gera você mesmo. Cobre todas as implementações onde configura ambos os lados da ligação – uma Autoridade de Certificação pública só entra em cena quando clientes de terceiros precisam de se ligar sem serem informados para confiar num certificado personalizado.
14.4.3.1. Criar um certificado autoassinado¶
Execute o OpenSSL na sua máquina de desenvolvimento. O subjectAltName (SAN) é o que os clientes TLS modernos verificam durante a verificação do nome de anfitrião, por isso defina-o para o(s) nome(s) de anfitrião e/ou endereço(s) IP que os clientes utilizarão para aceder à câmara (CN isolado é legado e é ignorado por muitos clientes). Substitua DNS:openmv / IP:192.168.1.50 pelo endereço ao qual os seus clientes realmente se ligam.
ECDSA P-256 – recomendado:
# 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 – mais forte, maior/mais 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 – compatibilidade máxima:
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
Um certificado de cliente (utilizado para autenticação mútua, abaixo) é criado exatamente com estes mesmos comandos – não há nada específico do cliente no próprio certificado. Basta gerar um segundo par chave/certificado independente com nomes diferentes (p.ex. client.key / client.crt) e utilizá-lo no cliente conforme mostrado no exemplo mTLS. O subjectAltName só é importante para o lado cujo nome de anfitrião o par verifica (o cliente verifica o nome do servidor; nada verifica o do cliente), por isso pode ser omitido num certificado apenas para cliente. O -subj / CN é igualmente apenas uma etiqueta num certificado de cliente – o lado servidor aqui verifica apenas que o certificado encadeia a uma CA de confiança, nunca faz corresponder o nome – por isso defina-o para o que identifica esse cliente (p.ex. /CN=sensor-01). Mantenha algum valor -subj independentemente, para que o OpenSSL possa gerar o certificado de forma não interativa.
O tempo de vida do certificado é definido por -days; os certificados expiram e têm de ser regenerados e reimplementados antes disso.
14.4.3.2. Converter para DER¶
Converta tanto o certificado como a chave privada para DER antes de os copiar para a câmara:
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. Copiar ficheiros para a câmara¶
Copie os ficheiros DER para o sistema de ficheiros da câmara – por exemplo arrastando-os para a unidade USB da OpenMV Cam, ou com mpremote cp server.der : e mpremote cp server.key.der :. No lado verificador, copie também o certificado CA / par em formato DER.
Os ficheiros DER não têm de residir no sistema de ficheiros gravável. O MicroPython também pode montar uma imagem ROMFS de apenas leitura em /rom, e os certificados colocados aí são carregados exatamente como qualquer outro ficheiro – p.ex. ctx.load_cert_chain("/rom/server.der", "/rom/server.key.der"). Uma imagem ROMFS é preparada na sua máquina de desenvolvimento e é de apenas leitura em tempo de execução, pelo que o certificado não pode ser alterado no dispositivo – útil para bloquear uma unidade de produção. Note que uma chave privada armazenada no ROMFS ainda é legível pelo código em execução na câmara; o ROMFS protege contra a modificação, não contra a extração. Um certificado residente em ROMFS só pode ser substituído reconstruindo e reinstalando o firmware.
14.4.3.4. Utilizar o certificado¶
Um cliente completo que define o relógio, abre um socket, verifica um servidor autoassinado e troca dados:
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()
Um servidor completo que apresenta o seu certificado e chave:
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()
Para autenticação mútua (mTLS) o servidor exige e verifica adicionalmente um certificado de cliente, e o cliente apresenta o seu próprio:
# 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")
Consulte a documentação do módulo ssl para a API completa.
Nota
Tudo nesta página aplica-se sem alterações ao DTLS (TLS sobre UDP). As chaves, certificados, formato DER, modelo de confiança, preocupações de expiração e as chamadas load_cert_chain / load_verify_locations são idênticos; apenas o transporte difere – envolve um socket socket.SOCK_DGRAM e seleciona ssl.PROTOCOL_DTLS_CLIENT / ssl.PROTOCOL_DTLS_SERVER em vez das constantes de protocolo TLS. A única complicação adicional é um cookie anti-falsificação do lado do servidor – a primeira ligação de um novo cliente deverá falhar e o cliente simplesmente tenta novamente; consulte Suporte a DTLS para detalhes.