ssl — Módulo SSL/TLS¶
Este módulo fornece acesso aos recursos de criptografia e autenticação de pares do Transport Layer Security (anteriormente e amplamente conhecido como “Secure Sockets Layer”) para sockets de rede, tanto no lado cliente quanto no lado servidor.
Dica
Novo no TLS na câmera? Comece pelo tutorial Trabalhando com certificados TLS. Ele guia você na escolha dos tipos de chave, na criação e conversão de certificados para o formato DER exigido pela câmera, em como colocá-los no dispositivo e em como verificar servidores e clientes – com exemplos completos e funcionais.
Nota
O MicroPython não implementa ssl.SSLError. Falhas de SSL/TLS são lançadas como OSError em vez disso.
Exemplos¶
Cliente TLS, verificando o certificado do servidor contra um certificado CA (no formato DER) armazenado no sistema de arquivos:
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()
Para uma conexão rápida e insegura (sem validação de certificado), a função de conveniência ssl.wrap_socket() pode ser usada em vez disso:
ssock = ssl.wrap_socket(sock, server_hostname="example.com")
Servidor TLS, apresentando seu próprio certificado e chave privada (formato 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()
Funções¶
- 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¶
Encapsula o sock fornecido e retorna um novo objeto wrapped-socket. A implementação dessa função consiste em primeiro criar um
SSLContexte então chamar o métodoSSLContext.wrap_socket()nesse objeto de contexto. Os argumentos sock, server_side e server_hostname são repassados sem alteração para a chamada do método. O argumento do_handshake é repassado como do_handshake_on_connect. Os argumentos restantes têm o seguinte comportamento:cert_reqs determina se o par (servidor ou cliente) deve apresentar um certificado válido. Observe que
ssl.CERT_NONEessl.CERT_OPTIONALnão validam nenhum certificado; somentessl.CERT_REQUIREDo faz.cadata é um objeto bytes contendo a cadeia de certificados CA (no formato DER) que validará o certificado do par. Atualmente, apenas um único certificado codificado em DER é suportado.
Classes¶
- class ssl.SSLContext(protocol: int, /)¶
Cria uma nova instância de SSLContext. O argumento protocol deve ser uma das constantes
PROTOCOL_*.- load_cert_chain(certfile: str | bytes, keyfile: str | bytes) None¶
Carrega uma chave privada e o certificado correspondente. O certfile é uma string com o caminho do arquivo do certificado. O keyfile é uma string com o caminho do arquivo da chave privada.
Diferença em relação ao CPython
Extensão do MicroPython: certfile e keyfile podem ser objetos bytes em vez de strings, caso em que são interpretados como os próprios dados do certificado/chave.
- load_verify_locations(cafile: str | None = None, cadata: bytes | None = None) None¶
Carrega a cadeia de certificados CA que validará o certificado do par. cafile é o caminho do arquivo dos certificados CA. cadata é um objeto bytes contendo os certificados CA. Apenas um desses argumentos deve ser fornecido.
- get_ciphers() List[str]¶
Obtém uma lista das cifras habilitadas, retornada como uma lista de strings.
- set_ciphers(ciphers: List[str]) None¶
Define as cifras disponíveis para sockets criados com este contexto. ciphers deve ser uma lista de strings no formato de suíte de cifras da IANA .
- 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¶
Recebe um stream sock (geralmente uma instância de socket.socket do tipo
SOCK_STREAM) e retorna uma instância de ssl.SSLSocket, encapsulando o stream subjacente. O objeto retornado tem os métodos usuais da interface stream, comoread(),write(), etc.server_side seleciona se o socket encapsulado está no lado servidor ou no lado cliente. Um socket SSL do lado servidor deve ser criado a partir de um socket normal retornado por
accept()em um socket de servidor de escuta não-SSL.do_handshake_on_connect determina se o handshake é feito como parte do
wrap_socketou se é adiado para ser feito como parte das leituras ou escritas iniciais. Para sockets bloqueantes, fazer o handshake imediatamente é o padrão. Para sockets não-bloqueantes (isto é, quando o sock passado parawrap_socketestá em modo não-bloqueante), o handshake geralmente deve ser adiado, pois caso contrário owrap_socketbloqueia até concluí-lo.server_hostname serve para uso como cliente e define o nome de host a ser verificado contra o certificado de servidor recebido. Ele também define o nome para Server Name Indication (SNI), permitindo que o servidor apresente o certificado adequado.
client_id é um argumento de extensão específico do MicroPython, usado apenas ao implementar um servidor DTLS. Veja Suporte a DTLS para detalhes.
Aviso
Por padrão, nenhuma validação de certificado é realizada (
ssl.CERT_NONE). Para uma conexão segura, você deve verificar o certificado do par definindo cert_reqs /SSLContext.verify_modecomossl.CERT_REQUIRED; caso contrário, a conexão fica vulnerável a ataques man-in-the-middle.O
wrap_socketdo CPython retorna um objetoSSLSocketque possui métodos típicos de sockets, comosend,recv, etc. Owrap_socketdo MicroPython retorna um objeto mais semelhante aoSSLObjectdo CPython, que não possui esses métodos de socket.
- verify_mode¶
Define ou obtém o comportamento de verificação dos certificados de pares. Deve ser uma das constantes
CERT_*.Nota
ssl.CERT_REQUIREDexige que a data/hora do dispositivo esteja corretamente configurada, por exemplo usando mpremote rtc --set ountptime, e oserver_hostnamedeve ser especificado quando no lado cliente.
Suporte a DTLS¶
Diferença em relação ao CPython
Esta é uma extensão do MicroPython.
Este módulo suporta DTLS nos modos cliente e servidor por meio das constantes PROTOCOL_DTLS_CLIENT e PROTOCOL_DTLS_SERVER, que podem ser usadas como argumento protocol de SSLContext.
Nesse caso, espera-se que o socket subjacente se comporte como um socket de datagrama (isto é, como o socket aberto com socket.socket usando socket.AF_INET como af e socket.SOCK_DGRAM como type).
Suporte a servidor DTLS¶
O suporte a servidor DTLS do MicroPython é configurado com “Hello Verify”, conforme exigido pelo DTLS 1.2. Isso é transparente para clientes DTLS, mas há considerações relevantes ao implementar um servidor DTLS no MicroPython:
O servidor deve passar um argumento adicional client_id ao chamar
SSLContext.wrap_socket(). Esse ID deve ser um objetobytes(ou similar) com um identificador específico do transporte que represente o cliente.A abordagem mais simples é converter a tupla
(client_ip, client_port)retornada porsocket.recv_from()em uma string de bytes, isto é:_, 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())
Na primeira vez que um cliente se conecta, a chamada do servidor a
wrap_socketfalhará com um erroOSError“Hello Verify Required”. Isso ocorre porque o cookie de “Hello Verify” do DTLS ainda não é conhecido pelo cliente. Se o mesmo cliente se conectar uma segunda vez, entãowrap_socketserá bem-sucedido.Os cookies DTLS para “Hello Verify” são associados ao objeto
SSLContext, portanto o mesmo objetoSSLContextdeve ser usado para encapsular uma conexão subsequente do mesmo cliente. A implementação do cookie inclui um tempo limite e tem uso constante de memória, independentemente de quantos clientes se conectam, então não há problema em reutilizar o mesmo objetoSSLContextdurante toda a vida útil do servidor.
Constantes¶
- ssl.PROTOCOL_TLS_CLIENT: int¶
Valor suportado para o parâmetro protocol, selecionando o modo cliente TLS.
- ssl.PROTOCOL_TLS_SERVER: int¶
Valor suportado para o parâmetro protocol, selecionando o modo servidor TLS.
- ssl.PROTOCOL_DTLS_CLIENT: int¶
Valor suportado para o parâmetro protocol, selecionando o modo cliente DTLS.
- ssl.PROTOCOL_DTLS_SERVER: int¶
Valor suportado para o parâmetro protocol, selecionando o modo servidor DTLS.
- ssl.CERT_NONE: int¶
Valor suportado para o parâmetro cert_reqs e para o atributo
SSLContext.verify_mode. Nenhuma verificação de certificado é realizada no par.
- ssl.CERT_OPTIONAL: int¶
Valor suportado para o parâmetro cert_reqs e para o atributo
SSLContext.verify_mode. A verificação de certificado é opcional. Observe que, na OpenMV Cam, isso se comporta comossl.CERT_NONE.
- ssl.CERT_REQUIRED: int¶
Valor suportado para o parâmetro cert_reqs e para o atributo
SSLContext.verify_mode. É exigido um certificado válido do par.