TLS Nativo

Suportado por

 

TsgcWebSocketClient

TsgcTCPClient

TsgcHTTP1Client

TsgcHTTP2Client

TsgcWebSocketServer

TsgcWebSocketHTTPServer

TsgcHTTPServer

TsgcWebSocketProxyServer

TsgcWebSocketLoadBalancerServer

 

*Requer o pacote sgcCrypto, que é fornecido a partir da edição Standard.

 

O mecanismo TLS nativo é uma implementação de TLS 1.3 e TLS 1.2 escrita inteiramente em Object Pascal sobre as units do sgcCrypto. Ele não é um binding: não há DLL do OpenSSL para implantar, nenhuma chamada ao SChannel e nenhuma biblioteca TLS da plataforma envolvida. O mesmo código-fonte compila do Delphi 7 ao RAD Studio 13 e roda em todas as plataformas que os componentes suportam.

 

O motivo para recorrer a ele é a troca de chaves. O mecanismo oferece os grupos híbridos pós-quânticos da RFC 10024, e X25519MLKEM768 é o primeiro grupo que ele propõe, de modo que um cliente que passe a usá-lo negocia uma troca de chaves pós-quântica contra qualquer servidor que suporte uma, sem nenhuma outra alteração na aplicação. Os grupos híbridos existem apenas no TLS 1.3, portanto uma conexão que recorre ao TLS 1.2 usa uma curva clássica. Veja Criptografia Pós-Quântica para saber o que são esses grupos e por que um híbrido é a migração sensata hoje.

 

Como habilitá-lo

 

Defina o IOHandler e nada mais muda. As propriedades de certificado, chave, certificado raiz, verificação e ALPN que você já usa continuam funcionando, o mecanismo lê as mesmas.

 


// client
oClient := TsgcWebSocketClient.Create(nil);
oClient.URL := 'wss://www.esegece.com:2053';
oClient.TLS := True;
oClient.TLSOptions.IOHandler := iohNativeTLS;
oClient.TLSOptions.VerifyCertificate := True;
oClient.TLSOptions.RootCertFile := 'cacert.pem';
oClient.Active := True;

// server
oServer := TsgcWebSocketServer.Create(nil);
oServer.SSL := True;
oServer.SSLOptions.IOHandler := iohNativeTLS;
oServer.SSLOptions.CertFile := 'certificate.pem';
oServer.SSLOptions.KeyFile := 'key.pem';
oServer.Active := True;

 

Em um servidor, o mecanismo também roda sobre os mecanismos orientados a eventos. Defina IOHandlerOptions.IOHandlerType como iohIOCP no Windows ou iohEPOLL no Linux e o handler é construído sobre o Provedor TLS correspondente, de modo que a mesma configuração funciona nos três mecanismos.

 

Selecionar iohNativeTLS em um build sem o pacote sgcCrypto gera uma exceção que diz exatamente isso, em vez de recorrer silenciosamente a outra biblioteca.

 

Versões de TLS

 

O mecanismo fala TLS 1.3 e TLS 1.2, como cliente e como servidor, no cliente Indy, no servidor Indy e nos mecanismos de servidor IOCP e EPOLL. O TLS 1.3 é sempre escolhido quando os dois lados o suportam. No mecanismo nativo, TLSOptions.Version em um cliente e SSLOptions.Version em um servidor é a versão mínima permitida.

 

Versão Negociado
tlsUndefined (o padrão) TLS 1.3 ou TLS 1.2
tls1_2 TLS 1.3 ou TLS 1.2
tls1_3 Somente TLS 1.3
tls1_0, tls1_1 Nada. Um cliente gera um erro de configuração ao se conectar, e um servidor ao iniciar, porque o mecanismo suporta apenas TLS 1.2 e TLS 1.3.

 

Isso é diferente de como o handler OpenSSL lê a propriedade. Com iohOpenSSL, tls1_2 significa exatamente TLS 1.2, enquanto com iohNativeTLS significa TLS 1.2 ou posterior. A lista de cipher suites decide o resto. Para obter somente TLS 1.2, mantenha Version em tls1_2 e informe apenas suites de TLS 1.2 em NativeTLS_Options.CipherSuites. Uma lista que informa apenas suites de TLS 1.3 resulta em somente TLS 1.3, e uma lista que informa suites das duas versões permite ambas. Version tls1_3 junto com uma lista de suites de TLS 1.2 não deixa nenhuma versão para negociar, e a conexão gera um erro ao iniciar.

 


// TLS 1.3 and TLS 1.2, tls1_2 is the lowest version allowed
oClient.TLSOptions.IOHandler := iohNativeTLS;
oClient.TLSOptions.Version := tls1_2;

// TLS 1.3 only
oClient.TLSOptions.Version := tls1_3;

// TLS 1.2 only: name TLS 1.2 suites and no TLS 1.3 suite
oClient.TLSOptions.Version := tls1_2;
oClient.TLSOptions.NativeTLS_Options.CipherSuites :=
  'TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256:TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256';

 

Grupos e cipher suites

 

TLSOptions.NativeTLS_Options em um cliente, e SSLOptions.NativeTLS_Options em um servidor, carregam as duas listas de preferência. As duas são strings no estilo do OpenSSL, itens separados por dois-pontos, o mais preferido primeiro, e uma única lista atende tanto o TLS 1.3 quanto o TLS 1.2. Deixe uma delas vazia para manter o padrão do mecanismo. Uma lista inválida gera um erro quando a conexão inicia, portanto um erro de digitação não é ignorado silenciosamente.

 

Propriedade Nomes aceitos Padrão
Groups X25519MLKEM768, SecP256r1MLKEM768, SecP384r1MLKEM1024, X25519, secp256r1, secp384r1. Os apelidos P-256, prime256v1 e P-384 também são aceitos, e os nomes são comparados sem diferenciar maiúsculas de minúsculas. Os três grupos híbridos são exclusivos do TLS 1.3. X25519MLKEM768:X25519:secp256r1:secp384r1
CipherSuites TLS 1.3: TLS_AES_256_GCM_SHA384, TLS_CHACHA20_POLY1305_SHA256, TLS_AES_128_GCM_SHA256. TLS 1.2: as suites da tabela abaixo. Tanto os nomes IANA quanto os nomes no estilo OpenSSL, como ECDHE-RSA-AES128-GCM-SHA256, são aceitos, e o mecanismo informa o nome IANA. TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256, mais os padrões de TLS 1.2 abaixo

 

O primeiro grupo da lista é aquele para o qual um cliente envia um key share, portanto deixar X25519MLKEM768 à frente é o que torna o primeiro voo já pós-quântico. Prefixar um item com um ponto de interrogação faz com que ele seja pulado quando este build não o possui, e qualquer outro item desconhecido ou indisponível faz a lista inteira falhar.

 


// only the hybrid post-quantum groups, and only AES-256-GCM
oClient.TLSOptions.NativeTLS_Options.Groups :=
  'X25519MLKEM768:SecP256r1MLKEM768:SecP384r1MLKEM1024';
oClient.TLSOptions.NativeTLS_Options.CipherSuites := 'TLS_AES_256_GCM_SHA384';

// tolerate a build where X25519 is not compiled in
oClient.TLSOptions.NativeTLS_Options.Groups := '?X25519MLKEM768:secp256r1';

 

Cipher suites do TLS 1.2

 

Por padrão, somente suites com sigilo direto e AEAD são habilitadas. As suites AES-CBC e a troca de chaves RSA existem para peers que não conseguem algo melhor, e só são oferecidas ou aceitas quando CipherSuites as nomeia.

 

Suites Habilitadas
TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 Por padrão, nessa ordem.
TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384, TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384 Somente quando nomeadas em CipherSuites.
TLS_RSA_WITH_AES_128_GCM_SHA256, TLS_RSA_WITH_AES_256_GCM_SHA384, TLS_RSA_WITH_AES_128_CBC_SHA, TLS_RSA_WITH_AES_256_CBC_SHA, TLS_RSA_WITH_AES_128_CBC_SHA256, TLS_RSA_WITH_AES_256_CBC_SHA256 Somente quando nomeadas em CipherSuites. A troca de chaves RSA não tem sigilo direto.

 

As suites ECDHE de uma conexão TLS 1.2 usam um grupo clássico da lista Groups, X25519, secp256r1 ou secp384r1, portanto mantenha pelo menos um deles na lista quando peers com TLS 1.2 precisarem se conectar. A troca de chaves RSA não usa grupo algum.

 


// TLS 1.3, and TLS 1.2 with AES-GCM plus AES-CBC for older clients
oServer.SSLOptions.IOHandler := iohNativeTLS;
oServer.SSLOptions.Version := tls1_2;
oServer.SSLOptions.NativeTLS_Options.CipherSuites :=
  'TLS_AES_256_GCM_SHA384:TLS_AES_128_GCM_SHA256:' +
  'TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384:TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256:' +
  'TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256:TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA';

 

Em um build feito com SGC_CRYPTO_FIPS, os dois grupos construídos sobre X25519, as suites ChaCha20-Poly1305 e a troca de chaves RSA são removidos da compilação, e os padrões passam a ser SecP256r1MLKEM768:SecP384r1MLKEM1024:secp256r1:secp384r1 e TLS_AES_256_GCM_SHA384:TLS_AES_128_GCM_SHA256, com as quatro suites ECDHE AES-GCM para TLS 1.2. Veja Modo FIPS.

 

Comportamento do TLS 1.2

 

O TLS 1.2 é implementado com as proteções que um peer moderno espera, e sem as partes do protocolo que causaram seus ataques conhecidos.

 

 

Retomada de sessão

O mecanismo retoma sessões com tíquetes de sessão sem estado, no TLS 1.3 (RFC 8446) e no TLS 1.2 (RFC 5077). Após um handshake completo, o servidor envia um tíquete, selado com AES-256-GCM sob uma chave que só o servidor possui. O cliente o mantém em um cache de sessão, indexado por host, porta e lista de ALPN, e o oferece na próxima conexão, que então pula a troca de certificados. Um tíquete que o servidor não consegue abrir, ou que expirou, resulta em um handshake completo com um novo tíquete.

Propriedade Padrão Significado
NativeTLS_Options.SessionTickets True Um servidor emite tíquetes e um cliente os armazena e oferece. False desativa a retomada, e todo handshake passa a ser completo.
NativeTLS_Options.TicketLifetime 7200 Somente no servidor. Segundos durante os quais um tíquete pode ser usado para retomar, no máximo 604800. A chave de tíquete do servidor gira a cada TicketLifetime segundos, e a chave anterior permanece válida por mais um período.

As opções são TLSOptions.NativeTLS_Options em um cliente e SSLOptions.NativeTLS_Options em um servidor. O cache de sessão pertence a cada componente cliente, portanto reconexões do mesmo TsgcWebSocketClient, TsgcHTTP1Client, TsgcTCPClient ou TsgcLDAPClient são retomadas, assim como a conexão upstream do TsgcWebSocketProxyServer.

A retomada no TLS 1.2 exige o extended master secret da RFC 7627. Uma sessão TLS 1.2 sem ele nunca é retomada e não recebe tíquete, e nenhum tíquete é emitido também quando o servidor pede um certificado de cliente. Um tíquete de TLS 1.3 nunca é aberto como uma sessão TLS 1.2, e vice-versa.

Se uma conexão foi retomada é informado pela propriedade Resumed do TsgcIdSSLIOHandlerSocketNativeTLS, o handler descrito abaixo.

O que foi negociado

 

Assim que o handshake termina, o handler do cliente, TsgcIdSSLIOHandlerSocketNativeTLS na unit sgcSSL_NativeTLS_Indy, informa o resultado. VersionName é TLSv1.2 ou TLSv1.3, CipherSuiteName é o nome IANA da suite, GroupName é o grupo da troca de chaves, vazio na troca de chaves RSA, e PeerSignatureSchemeName é o esquema de assinatura usado pelo servidor. O handler é aquele que o cliente passa para OnSSLAfterCreateHandler.

 


procedure TForm1.oClientSSLAfterCreateHandler(Sender: TObject;
  aType: TwsSSLHandler; aSSLHandler: TIdSSLIOHandlerSocketBase);
begin
  FHandler := aSSLHandler;
end;

procedure TForm1.oClientConnect(Connection: TsgcWSConnection);
var
  oHandler: TsgcIdSSLIOHandlerSocketNativeTLS;
begin
  if FHandler is TsgcIdSSLIOHandlerSocketNativeTLS then
  begin
    oHandler := TsgcIdSSLIOHandlerSocketNativeTLS(FHandler);
    Memo1.Lines.Add(oHandler.VersionName + ' ' + oHandler.CipherSuiteName +
      ' ' + oHandler.GroupName + ' ' + oHandler.PeerSignatureSchemeName);
  end;
end;

 

Certificados e verificação

 

Um servidor lê CertFile, KeyFile e Password. CertFile e KeyFile podem ser PEM, ou CertFile pode ser um arquivo .pfx ou .p12, reconhecido pela extensão. Um arquivo PKCS#12 contém o certificado, a cadeia e a chave privada, portanto KeyFile não é necessário, e ele é aberto com Password. É lido pelo leitor PKCS#12 em Object Pascal puro do sgcCrypto_PKCS12, sem OpenSSL. Um cliente que define VerifyCertificate como True verifica a cadeia do servidor contra as raízes de confiança descritas abaixo e verifica que o nome corresponde ao host ao qual se conectou. VerifyDepth é repassado, e um valor acima de 16 é limitado a 16.

 

As raízes de confiança são os certificados em RootCertFile. Definir NativeTLS_Options.UseSystemRoots como True adiciona as autoridades certificadoras em que o sistema operacional confia. O padrão é False. As duas se combinam: as raízes de RootCertFile são carregadas primeiro, as raízes do sistema são adicionadas ao mesmo repositório, e uma cadeia é aceita quando leva a qualquer uma delas. Quando nenhuma raiz do sistema é encontrada, nada é adicionado e nenhum erro é gerado, de modo que a verificação depende apenas de RootCertFile. A propriedade é TLSOptions.NativeTLS_Options.UseSystemRoots em um cliente e SSLOptions.NativeTLS_Options.UseSystemRoots em um servidor, onde as raízes servem apenas para validar certificados de cliente. De onde vêm as raízes do sistema depende da plataforma:

Para carregar você mesmo uma pasta de arquivos de certificado, TsgcTLS13TrustStore.AddDirectory(aDirectory, aRemovedDirectory) na unit sgcSSL_NativeTLS_Certificates lê todo arquivo PEM ou DER de aDirectory, ignora qualquer arquivo cujo nome também exista em aRemovedDirectory, e retorna quantos certificados adicionou. Funciona em todas as plataformas.

 

Para fixar o nome que o certificado deve carregar em vez de usar o host da conexão, defina TLSOptions.OpenSSL_Options.X509Checks.HostName ou IPAddress, com a flag correspondente em X509Checks.Mode. O mecanismo lê esses dois valores mesmo não sendo OpenSSL, portanto a propriedade significa a mesma coisa qualquer que seja o IOHandler selecionado.

 

Em um servidor, SSLOptions.VerifyCertificate pede um certificado ao cliente e o valida contra as raízes de confiança descritas acima, e SSLOptions.VerifyCertificate_Options.FailIfNoCertificate recusa um cliente que não envia nenhum. VerifyClientOnce não tem função aqui. O TLS 1.3 não tem renegociação, e o mecanismo a recusa no TLS 1.2, portanto nunca pede uma segunda vez.

 

O ALPN funciona como de costume por meio de ALPNProtocols em um cliente e por meio da seleção de ALPN do servidor, de modo que HTTP/2 sobre o mecanismo nativo negocia h2 da mesma forma que faz sobre o OpenSSL. Veja ALPN.

 

Modo QUIC

O mecanismo também executa o handshake TLS 1.3 do QUIC e do HTTP/3 (RFC 9001). Defina SSLQUICOptions.IOHandler em TsgcQUICClient, TLSOptions.IOHandler em TsgcHTTP3Client, QUICOptions.SSLOptions.IOHandler em TsgcQUICServer e TsgcHTTP3Server, ou SSLOptions.IOHandler em um servidor com HTTP3Options.Enabled, como iohNativeTLS.

Veja QUIC para a comparação dos mecanismos.

O que ele não faz

 

O mecanismo é deliberadamente restrito. Tudo o que está abaixo é um limite real, não uma configuração a procurar.

 

 

Nada do que o peer envia gera uma exceção. Uma falha de protocolo encerra a conexão com o alerta TLS apropriado, e o motivo é informado pelos eventos de erro do componente, como de costume.

A proteção de registros AES-GCM e AES-CBC usa as instruções AES-NI e PCLMULQDQ em builds do Windows feitos com Delphi quando a CPU as possui, veja sgcAES_HardwareAvailable em Criptografia Simétrica. Para um build em que nenhuma parte da biblioteca carrega o OpenSSL, veja Executar sem OpenSSL.