Ondersteund door
TsgcWebSocketLoadBalancerServer
*Vereist het sgcCrypto-pack, dat vanaf de Standard-editie wordt meegeleverd.
De native TLS-engine is een TLS 1.3- en TLS 1.2-implementatie die volledig in Object Pascal is geschreven bovenop de sgcCrypto-units. Het is geen binding: er is geen OpenSSL-DLL om te distribueren, geen SChannel-aanroep en geen platform-TLS-bibliotheek bij betrokken. Dezelfde broncode compileert van Delphi 7 tot en met RAD Studio 13 en draait op elk platform dat de componenten ondersteunen.
De reden om ernaar te grijpen is de sleuteluitwisseling. De engine biedt de hybride post-quantum groepen van RFC 10024, en X25519MLKEM768 is de eerste groep die ze voorstelt, zodat een client die erop overstapt een post-quantum sleuteluitwisseling onderhandelt met elke server die er een ondersteunt, zonder verdere wijziging aan de toepassing. De hybride groepen bestaan alleen in TLS 1.3, dus een verbinding die terugvalt op TLS 1.2 gebruikt een klassieke curve. Zie Post-quantumcryptografie voor wat die groepen zijn en waarom een hybride vandaag de verstandige migratie is.
Inschakelen
Stel de IOHandler in en er verandert verder niets. De eigenschappen voor certificaat, sleutel, rootcertificaat, verificatie en ALPN die u al gebruikt blijven werken, de engine leest dezelfde.
// 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;
Op een server draait de engine ook op de event-gestuurde engines. Stel IOHandlerOptions.IOHandlerType in op iohIOCP op Windows of iohEPOLL op Linux en de handler wordt gebouwd op de bijbehorende TLS-provider, zodat dezelfde configuratie op alle drie de engines werkt.
Het selecteren van iohNativeTLS in een build zonder het sgcCrypto-pack geeft een uitzondering die dat meldt, in plaats van stilzwijgend terug te vallen op een andere bibliotheek.
TLS-versies
De engine spreekt TLS 1.3 en TLS 1.2, als client en als server, op de Indy-client, op de Indy-server en op de IOCP- en EPOLL-server-engines. TLS 1.3 wordt altijd gekozen wanneer beide kanten het ondersteunen. Voor de native engine is TLSOptions.Version op een client en SSLOptions.Version op een server de laagste toegestane versie.
| Versie | Onderhandelt |
| tlsUndefined (de standaardwaarde) | TLS 1.3 of TLS 1.2 |
| tls1_2 | TLS 1.3 of TLS 1.2 |
| tls1_3 | Alleen TLS 1.3 |
| tls1_0, tls1_1 | Niets. Een client geeft een configuratiefout bij het verbinden, en een server bij het starten, omdat de engine uitsluitend TLS 1.2 en TLS 1.3 ondersteunt. |
Dit is niet hoe de OpenSSL-handler de eigenschap leest. Met iohOpenSSL betekent tls1_2 precies TLS 1.2, terwijl het met iohNativeTLS TLS 1.2 of hoger betekent. De cipher suite-lijst bepaalt de rest. Om alleen TLS 1.2 te krijgen, laat u Version op tls1_2 staan en noemt u in NativeTLS_Options.CipherSuites alleen TLS 1.2-suites. Een lijst die alleen TLS 1.3-suites noemt, geeft alleen TLS 1.3, en een lijst die suites van beide versies noemt, staat beide toe. Version tls1_3 samen met een lijst van TLS 1.2-suites laat geen versie over om te onderhandelen, en de verbinding geeft een fout bij het starten.
// 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';
Groepen en cipher suites
TLSOptions.NativeTLS_Options op een client, en SSLOptions.NativeTLS_Options op een server, dragen de twee voorkeurslijsten. Beide zijn tekenreeksen in de OpenSSL-stijl, items gescheiden door dubbele punten, de meest geprefereerde eerst, en één lijst bedient TLS 1.3 en TLS 1.2 samen. Laat er een leeg om de standaardwaarde van de engine te behouden. Een ongeldige lijst geeft een fout wanneer de verbinding start, zodat een typefout niet stilzwijgend wordt genegeerd.
| Eigenschap | Geaccepteerde namen | Standaardwaarde |
| Groups | X25519MLKEM768, SecP256r1MLKEM768, SecP384r1MLKEM1024, X25519, secp256r1, secp384r1. De aliassen P-256, prime256v1 en P-384 worden ook geaccepteerd, en namen worden vergeleken zonder te letten op hoofdletters of kleine letters. De drie hybride groepen gelden alleen voor 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: de suites in de tabel hieronder. Zowel IANA-namen als namen in OpenSSL-stijl zoals ECDHE-RSA-AES128-GCM-SHA256 worden geaccepteerd, en de engine meldt de IANA-naam. | TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256, plus de TLS 1.2-standaardwaarden hieronder |
De eerste groep in de lijst is degene waarvoor een client een sleuteldeel verstuurt, dus X25519MLKEM768 vooraan laten staan is wat de eerste vlucht al post-quantum maakt. Een item laten voorafgaan door een vraagteken slaat het over wanneer deze build het niet heeft, en elk ander onbekend of niet-beschikbaar item laat de hele lijst mislukken.
// 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';
TLS 1.2-cipher suites
Standaard zijn alleen suites met forward secrecy en AEAD ingeschakeld. De AES-CBC-suites en de RSA-sleuteluitwisseling zijn er voor tegenpartijen die het niet beter kunnen, en ze worden alleen aangeboden of geaccepteerd wanneer CipherSuites ze noemt.
| Suites | Ingeschakeld |
| 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 | Standaard, in deze volgorde. |
| 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 | Alleen wanneer genoemd 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 | Alleen wanneer genoemd in CipherSuites. De RSA-sleuteluitwisseling biedt geen forward secrecy. |
De ECDHE-suites van een TLS 1.2-verbinding gebruiken een klassieke groep uit de Groups-lijst, X25519, secp256r1 of secp384r1, houd dus ten minste een daarvan in de lijst wanneer er TLS 1.2-tegenpartijen verbinding moeten kunnen maken. De RSA-sleuteluitwisseling gebruikt helemaal geen groep.
// 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 een build die met SGC_CRYPTO_FIPS is gemaakt, worden de twee groepen die op X25519 zijn gebouwd, de ChaCha20-Poly1305-suites en de RSA-sleuteluitwisseling weggecompileerd, en veranderen de standaardwaarden in SecP256r1MLKEM768:SecP384r1MLKEM1024:secp256r1:secp384r1 en TLS_AES_256_GCM_SHA384:TLS_AES_128_GCM_SHA256, met de vier ECDHE-AES-GCM-suites voor TLS 1.2. Zie FIPS-modus.
TLS 1.2-gedrag
TLS 1.2 is geïmplementeerd met de beveiligingen die een moderne tegenpartij verwacht, en zonder de onderdelen van het protocol die verantwoordelijk waren voor de bekende aanvallen erop.
Extended master secret. RFC 7627 wordt door een client aangeboden en door een server geaccepteerd. Een tegenpartij die het niet ondersteunt, wordt nog steeds geaccepteerd.
Geen heronderhandeling. Een heronderhandelingsverzoek wordt geweigerd met een no_renegotiation-waarschuwing. Sessiehervatting gebruikt sessietickets en vereist het extended master secret, zie Sessiehervatting hieronder.
Geen SHA-1-handtekeningen. Volgens RFC 9155 worden SHA-1-handtekeningen noch verzonden noch geaccepteerd. Een TLS 1.2 ClientHello zonder de signature_algorithms-extensie wordt geweigerd, omdat dat alleen SHA-1 zou betekenen.
Downgradebescherming. De downgradebescherming van RFC 8446 sectie 4.1.3 werkt aan beide kanten. Een server die ook TLS 1.3 toestaat, markeert elk TLS 1.2-antwoord, en een client die TLS 1.3 heeft aangeboden weigert een TLS 1.2-antwoord dat die markering draagt.
Constante tijd voor CBC. CBC-records worden in constante tijd ontsleuteld, wat Lucky Thirteen afsluit, en de RSA-sleuteluitwisseling past de tegenmaatregel van RFC 5246 sectie 7.4.7.1 toe.
Key usage van het certificaat. Voor de RSA-sleuteluitwisseling heeft het servercertificaat de key usage keyEncipherment nodig. De ECDHE-suites en TLS 1.3 hebben digitalSignature nodig.
Functies die alleen in TLS 1.3 bestaan. KeyUpdate en het exporteren van keying material bestaan alleen bij een TLS 1.3-sessie.
Sessiehervatting
De engine hervat sessies met stateless sessietickets, in TLS 1.3 (RFC 8446) en in TLS 1.2 (RFC 5077). Na een volledige handshake stuurt de server een ticket, verzegeld met AES-256-GCM onder een sleutel die alleen de server heeft. De client bewaart het in een sessiecache, gesleuteld op host, poort en ALPN-lijst, en biedt het aan bij de volgende verbinding, waarna de certificaatuitwisseling wordt overgeslagen. Een ticket dat de server niet kan openen, of een verlopen ticket, geeft een volledige handshake met een nieuw ticket.
| Eigenschap | Standaardwaarde | Betekenis |
| NativeTLS_Options.SessionTickets | True | Een server geeft tickets uit en een client bewaart en biedt ze aan. False schakelt hervatting uit, elke handshake is dan volledig. |
| NativeTLS_Options.TicketLifetime | 7200 | Alleen server. Aantal seconden dat een ticket kan worden gebruikt om te hervatten, ten hoogste 604800. De ticketsleutel van de server roteert elke TicketLifetime seconden, en de vorige sleutel blijft nog één periode geldig. |
De opties zijn TLSOptions.NativeTLS_Options op een client en SSLOptions.NativeTLS_Options op een server. De sessiecache hoort bij elk clientonderdeel, dus herverbindingen van dezelfde TsgcWebSocketClient, TsgcHTTP1Client, TsgcTCPClient of TsgcLDAPClient worden hervat, en dat geldt ook voor de upstream-verbinding van TsgcWebSocketProxyServer.
TLS 1.2-hervatting vereist het extended master secret van RFC 7627. Een TLS 1.2-sessie zonder dit wordt nooit hervat en krijgt geen ticket, en er wordt ook geen ticket uitgegeven wanneer de server om een clientcertificaat vraagt. Een TLS 1.3-ticket opent nooit als TLS 1.2-sessie, en omgekeerd.
Of een verbinding is hervat, wordt gemeld via de eigenschap Resumed van TsgcIdSSLIOHandlerSocketNativeTLS, de hieronder beschreven handler.
Wat er is onderhandeld
Zodra de handshake is voltooid, meldt de client-handler, TsgcIdSSLIOHandlerSocketNativeTLS in de unit sgcSSL_NativeTLS_Indy, het resultaat. VersionName is TLSv1.2 of TLSv1.3, CipherSuiteName is de IANA-naam van de suite, GroupName is de sleuteluitwisselingsgroep, leeg bij de RSA-sleuteluitwisseling, en PeerSignatureSchemeName is het handtekeningschema dat de server gebruikte. De handler is degene die de client doorgeeft aan 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;
Certificaten en verificatie
Een server leest CertFile, KeyFile en Password. CertFile en KeyFile kunnen PEM zijn, of CertFile kan een .pfx- of .p12-bestand zijn, herkend aan de extensie. Een PKCS#12-bestand bevat het certificaat, de keten en de private key, dus KeyFile is dan niet nodig, en het wordt geopend met Password. Het wordt gelezen door de pure Pascal PKCS#12-lezer van sgcCrypto_PKCS12, zonder OpenSSL. Een client die VerifyCertificate op True zet, controleert de serverketen tegen de hieronder beschreven vertrouwde roots en controleert dat de naam overeenkomt met de host waarmee hij verbinding heeft gemaakt. VerifyDepth wordt doorgegeven, en een waarde boven 16 wordt op 16 begrensd.
De vertrouwde roots zijn de certificaten in RootCertFile. Door NativeTLS_Options.UseSystemRoots op True te zetten, worden de certificeringsinstanties toegevoegd die het besturingssysteem vertrouwt. Standaard is dit False. Beide combineren: de roots uit RootCertFile worden eerst geladen, de systeemroots worden aan hetzelfde archief toegevoegd, en een keten wordt geaccepteerd wanneer deze naar een van beide leidt. Wanneer geen systeemroot wordt gevonden, wordt niets toegevoegd en geen fout gegenereerd, zodat de verificatie alleen op RootCertFile steunt. De eigenschap is TLSOptions.NativeTLS_Options.UseSystemRoots op een client en SSLOptions.NativeTLS_Options.UseSystemRoots op een server, waar de roots alleen dienen om clientcertificaten te valideren. Waar de systeemroots vandaan komen, hangt af van het platform:
Windows. Het ROOT-systeemarchief, de Trusted Root Certification Authorities, gelezen via crypt32.dll.
macOS. De ankercertificaten van Security.framework, met /etc/ssl/cert.pem als terugval.
iOS. De systeemroots kunnen niet worden opgesomd. Wanneer geen root is geladen, wordt een keten die de engine alleen afwijst omdat de root niet wordt vertrouwd, geëvalueerd door Security.framework met het SSL-beleid en de hostnaam. De key usage- en hostnaamcontroles van de engine lopen daarna nog steeds.
Android. De certificaatbestanden van /apex/com.android.conscrypt/cacerts (Android 14 en later) of /system/etc/security/cacerts, PEM of DER. Roots die de gebruiker heeft uitgeschakeld, vermeld in /data/misc/user/0/cacerts-removed, worden overgeslagen, en de gebruikersroots van /data/misc/user/0/cacerts-added worden toegevoegd wanneer de toepassing die mappen kan lezen.
Linux en andere platforms. Het eerste van deze bestanden dat bestaat en ten minste één certificaat bevat: /etc/ssl/certs/ca-certificates.crt, /etc/pki/tls/certs/ca-bundle.crt, /etc/ssl/ca-bundle.pem, /etc/pki/tls/cacert.pem, /etc/ssl/cert.pem en /etc/ssl/certs/ca-bundle.crt.
Om zelf een map met certificaatbestanden te laden, leest TsgcTLS13TrustStore.AddDirectory(aDirectory, aRemovedDirectory) in de unit sgcSSL_NativeTLS_Certificates elk PEM- of DER-bestand van aDirectory, slaat elk bestand over waarvan de naam ook in aRemovedDirectory voorkomt, en geeft terug hoeveel certificaten het heeft toegevoegd. Het werkt op elk platform.
Om de naam vast te leggen die het certificaat moet dragen, in plaats van de host van de verbinding te gebruiken, stelt u TLSOptions.OpenSSL_Options.X509Checks.HostName of IPAddress in, met de bijbehorende vlag in X509Checks.Mode. De engine leest die twee waarden ook al is ze geen OpenSSL, zodat de eigenschap hetzelfde betekent welke IOHandler ook is geselecteerd.
Op een server vraagt SSLOptions.VerifyCertificate de client om een certificaat en valideert dit tegen de hierboven beschreven vertrouwde roots, en SSLOptions.VerifyCertificate_Options.FailIfNoCertificate weigert een client die er geen verstuurt. VerifyClientOnce heeft hier niets te doen. TLS 1.3 kent geen heronderhandeling, en de engine weigert die bij TLS 1.2, dus er wordt nooit een tweede keer gevraagd.
ALPN werkt zoals gebruikelijk via ALPNProtocols op een client en via de ALPN-selectie op de server, zodat HTTP/2 over de native engine h2 op dezelfde manier onderhandelt als over OpenSSL. Zie ALPN.
QUIC-modus
De engine voert ook de TLS 1.3-handshake van QUIC en HTTP/3 uit (RFC 9001). Zet SSLQUICOptions.IOHandler op TsgcQUICClient, TLSOptions.IOHandler op TsgcHTTP3Client, QUICOptions.SSLOptions.IOHandler op TsgcQUICServer en TsgcHTTP3Server, of SSLOptions.IOHandler op een server met HTTP3Options.Enabled, op iohNativeTLS.
De sgc QUIC-engine wordt altijd gebruikt, wat OpenSSL ook exporteert. Op een server meldt QUICMode qmCustom.
De handshake draagt de QUIC-transportparameters, vereist ALPN en meldt TLS-waarschuwingen als QUIC-foutcodes. Het servercertificaat wordt gelezen uit PEM of PFX, en de tegenpartij wordt geverifieerd tegen RootCertFile of de systeemroots, hostnaam inbegrepen.
Sessietickets, hervatting en 0-RTT werken zoals bij OpenSSL. De server stuurt een ticket na de handshake, de client bewaart het samen met de ALPN en de servertransportparameters, en 0-RTT wordt maar één keer per ticket geaccepteerd. QUIC-tickets gaan nooit over naar TLS over TCP.
De pakketbescherming is ook native, zodat QUIC en HTTP/3 geen OpenSSL laden. Hiervoor is geen define nodig.
Zie QUIC voor de vergelijking van de engines.
Wat het niet doet
De engine is bewust smal gehouden. Alles hieronder is een echte beperking, geen instelling om naar te zoeken.
Alleen TLS 1.3 en TLS 1.2. Er is geen SSL, geen TLS 1.0 en geen TLS 1.1. Een client geeft een fout bij het verbinden, en een server bij het starten, wanneer Version tls1_0 of tls1_1 is. Een tegenpartij die noch TLS 1.3 noch TLS 1.2 kan, kan geen verbinding maken.
Geen PSK zonder ticket. Hervatting werkt alleen met de sessietickets die de engine zelf uitgeeft, zie Sessiehervatting hierboven. Externe pre-shared keys worden niet ondersteund.
Geen 0-RTT over TCP. Bij een TLS-verbinding over TCP wordt early data niet verzonden en niet geaccepteerd. Clientauthenticatie na de handshake is evenmin geïmplementeerd.
QUIC via zijn eigen modus. QUIC en HTTP/3 gebruiken de hierboven beschreven QUIC-modus, geconfigureerd op die componenten.
Geen systeemroots standaard. Tenzij NativeTLS_Options.UseSystemRoots True is, komen vertrouwde roots alleen uit RootCertFile en nergens anders vandaan, dus een client die certificaten verifieert moet een rootbestand meeleveren of ernaar verwijzen. Dat is het ene verschil in gedrag dat een werkende OpenSSL- of SChannel-configuratie het snelst zal opmerken.
OnSSLVerifyPeer wordt niet gegenereerd. Die gebeurtenis hoort bij de OpenSSL-handler. De native engine heeft zijn eigen verificatiehaken, die de volledige keten van de tegenpartij en het oordeel van de engine in één keer ontvangen in plaats van één certificaat per diepte, dus een toepassing die de vertrouwensbeslissing overneemt moet die gebruiken en niet de gebeurtenis van de component.
Niets wat de tegenpartij verstuurt geeft een uitzondering. Een protocolfout beëindigt de verbinding met de juiste TLS-waarschuwing, en de reden wordt zoals gebruikelijk gemeld via de foutgebeurtenissen van de component.
De AES-GCM- en AES-CBC-recordbescherming gebruikt de AES-NI- en PCLMULQDQ-instructies op Windows-builds gemaakt met Delphi wanneer de CPU ze heeft, zie sgcAES_HardwareAvailable in Symmetrische versleuteling. Voor een build waarin geen enkel onderdeel van de bibliotheek OpenSSL laadt, zie Werken zonder OpenSSL.