TLS nativo

Supportato da

 

TsgcWebSocketClient

TsgcTCPClient

TsgcHTTP1Client

TsgcHTTP2Client

TsgcWebSocketServer

TsgcWebSocketHTTPServer

TsgcHTTPServer

TsgcWebSocketProxyServer

TsgcWebSocketLoadBalancerServer

 

*Richiede il pack sgcCrypto, disponibile a partire dall'edizione Standard.

 

Il motore TLS nativo è un'implementazione di TLS 1.3 e TLS 1.2 scritta interamente in Object Pascal sopra le unit sgcCrypto. Non è un binding: non c'è alcuna DLL OpenSSL da distribuire, nessuna chiamata a SChannel e nessuna libreria TLS di piattaforma coinvolta. Lo stesso sorgente compila da Delphi 7 a RAD Studio 13 e gira su tutte le piattaforme supportate dai componenti.

 

Il motivo per adottarlo è lo scambio di chiavi. Il motore offre i gruppi ibridi post-quantistici della RFC 10024, e X25519MLKEM768 è il primo gruppo che propone, quindi un client che passa a questo motore negozia uno scambio di chiavi post-quantistico con qualsiasi server che ne supporti uno, senza altre modifiche all'applicazione. I gruppi ibridi esistono solo in TLS 1.3, quindi una connessione che ripiega su TLS 1.2 utilizza una curva classica. Vedere Crittografia post-quantistica per capire che cosa sono quei gruppi e perché un ibrido è oggi la migrazione sensata.

 

Come attivarlo

 

Impostare l'IOHandler e nient'altro cambia. Le proprietà di certificato, chiave, certificato radice, verifica e ALPN già in uso continuano a funzionare, il motore legge le stesse.

 


// 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;

 

Su un server il motore funziona anche con gli engine a eventi. Impostare IOHandlerOptions.IOHandlerType a iohIOCP su Windows o a iohEPOLL su Linux e l'handler viene costruito sul provider TLS corrispondente, così la stessa configurazione funziona su tutti e tre gli engine.

 

Selezionare iohNativeTLS in una build senza il pack sgcCrypto genera un'eccezione che lo dichiara, anziché ripiegare silenziosamente su un'altra libreria.

 

Versioni TLS

 

Il motore parla TLS 1.3 e TLS 1.2, come client e come server, sul client Indy, sul server Indy e sugli engine server IOCP ed EPOLL. TLS 1.3 viene sempre scelto quando entrambe le parti lo supportano. Per il motore nativo, TLSOptions.Version su un client e SSLOptions.Version su un server è la versione minima consentita.

 

Versione Negozia
tlsUndefined (il valore predefinito) TLS 1.3 o TLS 1.2
tls1_2 TLS 1.3 o TLS 1.2
tls1_3 Solo TLS 1.3
tls1_0, tls1_1 Niente. Un client genera un errore di configurazione quando si connette e un server quando si avvia, perché il motore supporta solo TLS 1.2 e TLS 1.3.

 

Non è così che l'handler OpenSSL legge questa proprietà. Con iohOpenSSL, tls1_2 significa esattamente TLS 1.2, mentre con iohNativeTLS significa TLS 1.2 o successivo. A decidere il resto è la lista delle cipher suite. Per ottenere solo TLS 1.2, mantenere Version a tls1_2 e indicare in NativeTLS_Options.CipherSuites solo suite di TLS 1.2. Una lista che indica solo suite di TLS 1.3 dà solo TLS 1.3, e una lista che indica suite di entrambe le versioni permette entrambe. Version a tls1_3 insieme a una lista di suite di TLS 1.2 non lascia alcuna versione da negoziare, e la connessione genera un'eccezione all'avvio.

 


// 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';

 

Gruppi e cipher suite

 

TLSOptions.NativeTLS_Options su un client, e SSLOptions.NativeTLS_Options su un server, contengono le due liste di preferenza. Entrambe sono stringhe nello stile OpenSSL, con gli elementi separati da due punti e il più preferito per primo. Lasciarne una vuota per mantenere il valore predefinito del motore. Una lista non valida genera un'eccezione all'avvio della connessione, quindi un errore di battitura non viene ignorato in silenzio.

 

Proprietà Nomi accettati Predefinito
Groups X25519MLKEM768, SecP256r1MLKEM768, SecP384r1MLKEM1024, X25519, secp256r1, secp384r1. Sono accettati anche gli alias P-256, prime256v1 e P-384, e i nomi vengono confrontati senza distinzione fra maiuscole e minuscole. I tre gruppi ibridi sono esclusivi di 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: le suite della tabella seguente. Sono accettati sia i nomi IANA sia i nomi in stile OpenSSL come ECDHE-RSA-AES128-GCM-SHA256, e il motore riporta il nome IANA. TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256, più i valori predefiniti di TLS 1.2 indicati sotto

 

Il primo gruppo della lista è quello per cui un client invia una key share, quindi lasciare X25519MLKEM768 in testa è ciò che rende già post-quantistico il primo flight. Anteporre un punto interrogativo a un elemento lo salta quando questa build non lo contiene, mentre qualsiasi altro elemento sconosciuto o non disponibile fa fallire l'intera lista.

 


// 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 suite di TLS 1.2

 

Per impostazione predefinita sono abilitate solo le suite AEAD con forward secrecy. Le suite AES-CBC e lo scambio di chiavi RSA esistono per i peer che non possono fare di meglio, e vengono proposte o accettate solo quando CipherSuites le indica.

 

Suite Abilitate
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 Per impostazione predefinita, in questo ordine.
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 Solo quando indicate in 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 Solo quando indicate in CipherSuites. Lo scambio di chiavi RSA non ha forward secrecy.

 

Le suite ECDHE di una connessione TLS 1.2 utilizzano un gruppo classico della lista Groups, X25519, secp256r1 o secp384r1, quindi mantenerne almeno uno nella lista quando devono potersi connettere peer con TLS 1.2. Lo scambio di chiavi RSA non utilizza alcun gruppo.

 


// 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';

 

In una build creata con SGC_CRYPTO_FIPS i due gruppi costruiti su X25519, le suite ChaCha20-Poly1305 e lo scambio di chiavi RSA vengono esclusi dalla compilazione, e i valori predefiniti diventano SecP256r1MLKEM768:SecP384r1MLKEM1024:secp256r1:secp384r1 e TLS_AES_256_GCM_SHA384:TLS_AES_128_GCM_SHA256, con le quattro suite ECDHE AES-GCM per TLS 1.2. Vedere Modalità FIPS.

 

Comportamento di TLS 1.2

 

TLS 1.2 è implementato con le protezioni che un peer moderno si aspetta, e senza le parti del protocollo che hanno causato i suoi attacchi noti.

 

 

Ripresa di sessione

Il motore riprende le sessioni con session ticket stateless, in TLS 1.3 (RFC 8446) e in TLS 1.2 (RFC 5077). Dopo un handshake completo il server invia un ticket, sigillato con AES-256-GCM sotto una chiave che solo il server possiede. Il client lo conserva in una cache di sessione, indicizzata per host, porta ed elenco ALPN, e lo propone alla connessione successiva, che così salta lo scambio del certificato. Un ticket che il server non riesce ad aprire, o uno scaduto, produce un handshake completo con un nuovo ticket.

Proprietà Predefinito Significato
NativeTLS_Options.SessionTickets True Un server emette i ticket e un client li memorizza e li propone. False disattiva la ripresa, ogni handshake diventa allora completo.
NativeTLS_Options.TicketLifetime 7200 Solo server. Secondi durante i quali un ticket può essere usato per la ripresa, al massimo 604800. La chiave del ticket del server ruota ogni TicketLifetime secondi, e la chiave precedente resta valida per un altro periodo.

Le opzioni sono TLSOptions.NativeTLS_Options su un client e SSLOptions.NativeTLS_Options su un server. La cache di sessione appartiene a ciascun componente client, quindi le riconnessioni dello stesso TsgcWebSocketClient, TsgcHTTP1Client, TsgcTCPClient o TsgcLDAPClient riprendono, e così pure la connessione upstream di TsgcWebSocketProxyServer.

La ripresa in TLS 1.2 richiede l'extended master secret della RFC 7627. Una sessione TLS 1.2 priva di questo non viene mai ripresa e non riceve alcun ticket, e nessun ticket viene emesso nemmeno quando il server chiede un certificato client. Un ticket TLS 1.3 non si apre mai come sessione TLS 1.2, e viceversa.

Se una connessione è stata ripresa viene riportato dalla proprietà Resumed di TsgcIdSSLIOHandlerSocketNativeTLS, l'handler descritto più sotto.

Che cosa è stato negoziato

 

Una volta completato l'handshake, l'handler del client, TsgcIdSSLIOHandlerSocketNativeTLS nell'unit sgcSSL_NativeTLS_Indy, riporta il risultato. VersionName è TLSv1.2 o TLSv1.3, CipherSuiteName è il nome IANA della suite, GroupName è il gruppo dello scambio di chiavi, vuoto per lo scambio di chiavi RSA, e PeerSignatureSchemeName è lo schema di firma utilizzato dal server. L'handler è quello che il client passa a 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;

 

Certificati e verifica

 

Un server legge CertFile, KeyFile e Password. CertFile e KeyFile possono essere in formato PEM, oppure CertFile può essere un file .pfx o .p12, riconosciuto dall'estensione. Un file PKCS#12 contiene il certificato, la catena e la chiave privata, quindi KeyFile non serve, e viene aperto con Password. Viene letto dal lettore PKCS#12 in Pascal puro di sgcCrypto_PKCS12, senza OpenSSL. Un client che imposta VerifyCertificate a True verifica la catena del server rispetto alle radici di fiducia descritte più sotto e verifica che il nome corrisponda all'host a cui si è connesso. VerifyDepth viene applicato, e un valore superiore a 16 viene limitato a 16.

 

Le radici di fiducia sono i certificati presenti in RootCertFile. Impostare NativeTLS_Options.UseSystemRoots a True aggiunge le autorità di certificazione di cui si fida il sistema operativo. Il valore predefinito è False. Le due si combinano: le radici di RootCertFile vengono caricate per prime, le radici di sistema vengono aggiunte allo stesso archivio, e una catena viene accettata quando porta a una qualsiasi di esse. Quando non viene trovata alcuna radice di sistema, non viene aggiunto nulla e non viene generato alcun errore, quindi la verifica si basa unicamente su RootCertFile. La proprietà è TLSOptions.NativeTLS_Options.UseSystemRoots su un client e SSLOptions.NativeTLS_Options.UseSystemRoots su un server, dove le radici servono solo a validare i certificati client. Da dove provengono le radici di sistema dipende dalla piattaforma:

Per caricare autonomamente una cartella di file di certificati, TsgcTLS13TrustStore.AddDirectory(aDirectory, aRemovedDirectory) nell'unit sgcSSL_NativeTLS_Certificates legge ogni file PEM o DER di aDirectory, salta ogni file il cui nome esiste anche in aRemovedDirectory, e restituisce quanti certificati ha aggiunto. Funziona su ogni piattaforma.

 

Per fissare il nome che il certificato deve riportare anziché utilizzare l'host della connessione, impostare TLSOptions.OpenSSL_Options.X509Checks.HostName o IPAddress, con il flag corrispondente in X509Checks.Mode. Il motore legge quei due valori anche se non è OpenSSL, quindi la proprietà significa la stessa cosa qualunque sia l'IOHandler selezionato.

 

Su un server, SSLOptions.VerifyCertificate chiede un certificato al client e lo valida rispetto alle radici di fiducia descritte sopra, e SSLOptions.VerifyCertificate_Options.FailIfNoCertificate rifiuta un client che non ne invia alcuno. VerifyClientOnce non ha nulla da fare qui. TLS 1.3 non prevede la rinegoziazione e il motore la rifiuta in TLS 1.2, quindi non chiede mai una seconda volta.

 

ALPN funziona come sempre tramite ALPNProtocols su un client e tramite la selezione ALPN del server, quindi HTTP/2 sul motore nativo negozia h2 esattamente come su OpenSSL. Vedere ALPN.

 

Modalità QUIC

Il motore esegue anche l'handshake TLS 1.3 di QUIC e HTTP/3 (RFC 9001). Impostare SSLQUICOptions.IOHandler su TsgcQUICClient, TLSOptions.IOHandler su TsgcHTTP3Client, QUICOptions.SSLOptions.IOHandler su TsgcQUICServer e TsgcHTTP3Server, oppure SSLOptions.IOHandler su un server con HTTP3Options.Enabled, a iohNativeTLS.

Vedere QUIC per il confronto tra motori.

Che cosa non fa

 

Il motore è volutamente ristretto. Tutto ciò che segue è un limite reale, non un'impostazione da cercare.

 

 

Nulla di ciò che il peer invia genera un'eccezione. Un errore di protocollo chiude la connessione con l'alert TLS appropriato, e il motivo viene riportato come sempre attraverso gli eventi di errore del componente.

La protezione dei record AES-GCM e AES-CBC utilizza le istruzioni AES-NI e PCLMULQDQ sulle build Windows create con Delphi quando la CPU le supporta, vedere sgcAES_HardwareAvailable in Crittografia simmetrica. Per una build in cui nessuna parte della libreria carica OpenSSL, vedere Eseguire senza OpenSSL.