Zlyhania spojenia SSL

Java Top

Práve som oznámil nové Naučte sa jar kurz zameraný na základy jari 5 a Spring Boot 2:

>> SKONTROLUJTE KURZ

1. Prehľad

Secured Socket Layer (SSL) je kryptografický protokol, ktorý poskytuje bezpečnosť pri komunikácii cez sieť. V tomto tutoriáli si rozoberieme rôzne scenáre, ktoré môžu viesť k zlyhaniu spojenia SSL a ako na to.

Upozorňujeme, že náš Úvod do protokolu SSL pomocou JSSE podrobnejšie popisuje základy protokolu SSL.

2. Terminológia

Je dôležité poznamenať, že kvôli bezpečnostným zraniteľnostiam je SSL ako štandard nahradený protokolom TLS (Transport Layer Security). Väčšina programovacích jazykov vrátane Java má knižnice, ktoré podporujú protokol SSL aj TLS.

Od vzniku protokolu SSL malo veľa produktov a jazykov, ako napríklad OpenSSL a Java, odkazy na protokol SSL, ktoré si ponechali aj po prevzatí protokolu TLS. Z tohto dôvodu budeme vo zvyšku tohto tutoriálu používať termín SSL na všeobecné označenie kryptografických protokolov.

3. Inštalácia

Na účely tohto tutoriálu vytvoríme jednoduchý server a klientske aplikácie pomocou Java Socket API na simuláciu sieťového pripojenia.

3.1. Vytvorenie klienta a servera

V Jave môžeme použiť szásuvky na vytvorenie komunikačného kanála medzi serverom a klientom po sieti. Patice sú súčasťou Java Secure Socket Extension (JSSE) v Jave.

Začnime definovaním jednoduchého servera:

int port = 8443; ServerSocketFactory factory = SSLServerSocketFactory.getDefault (); try (ServerSocket listener = factory.createServerSocket (port)) {SSLServerSocket sslListener = (SSLServerSocket) poslucháč; sslListener.setNeedClientAuth (true); sslListener.setEnabledCipherSuites (nový reťazec [] {"TLS_DHE_DSS_WITH_AES_256_CBC_SHA256"}); sslListener.setEnabledProtocols (nový reťazec [] {"TLSv1.2"}); while (true) {try (Socket socket = sslListener.accept ()) {PrintWriter out = new PrintWriter (socket.getOutputStream (), true); out.println („Hello World!“); }}}

Server definovaný vyššie vráti správu „Hello World!“ pripojenému klientovi.

Ďalej definujeme základného klienta, ktorého pripojíme k nášmu SimpleServer:

Reťazec hostiteľ = "localhost"; int port = 8443; SocketFactory factory = SSLSocketFactory.getDefault (); try (Socket connection = factory.createSocket (host, port)) {((SSLSocket) connection) .setEnabledCipherSuites (new String [] {"TLS_DHE_DSS_WITH_AES_256_CBC_SHA256"}); ((SSLSocket) pripojenie) .setEnabledProtocols (nový reťazec [] {"TLSv1.2"}); SSLParameters sslParams = nové SSLParameters (); sslParams.setEndpointIdentificationAlgorithm ("HTTPS"); ((SSLSocket) pripojenie) .setSSLParameters (sslParams); BufferedReader vstup = nový BufferedReader (nový InputStreamReader (connection.getInputStream ())); návrat input.readLine (); }

Náš klient vytlačí správu vrátenú serverom.

3.2. Vytváranie certifikátov v Jave

SSL poskytuje utajenie, integritu a autenticitu v sieťovej komunikácii. Dôležitú úlohu pri stanovení autenticity majú certifikáty.

Spravidla sú tieto certifikáty zakúpené a podpísané certifikačnou autoritou, ale pre tento tutoriál budeme používať certifikáty s vlastným podpisom.

Aby sme to dosiahli, môžeme použiť kľúčový nástroj, ktorá sa dodáva s JDK:

$ keytool -genkey -keypass heslo \ -storepass heslo \ -keystore serverkeystore.jks

Vyššie uvedený príkaz spustí interaktívny shell na zhromažďovanie informácií o certifikáte, ako je bežný názov (CN) a rozlišujúci názov (DN). Keď poskytneme všetky príslušné podrobnosti, vygeneruje sa súbor serverkeystore.jks, ktorý obsahuje súkromný kľúč servera a jeho verejný certifikát.

Poznač si to serverkeystore.jks je uložený vo formáte Java Key Store (JKS), ktorý je vlastníctvom Java. V týchto dňoch, keytool vám pripomenie, že by sme mali zvážiť použitie PKCS # 12, ktorý tiež podporuje.

Môžeme ďalej používať keytool extrahovať verejný certifikát z vygenerovaného súboru úložiska kľúčov:

$ keytool -export -storepass heslo \ -súbor server.cer \ -keystore serverkeystore.jks

Vyššie uvedený príkaz exportuje verejný certifikát z úložiska kľúčov ako súbor server.cer. Použime exportovaný certifikát pre klienta tak, že ho pridáme do jeho úložiska dôvery:

$ keytool -import -v -trustcacerts \ -file server.cer \ -keypass heslo \ -storepass heslo \ -keystore clienttruststore.jks

Teraz sme vygenerovali úložisko kľúčov pre server a zodpovedajúce úložisko dôveryhodností pre klienta. Keď diskutujeme o možných zlyhaniach spojenia rúk, prejdeme k použitiu týchto generovaných súborov.

A ďalšie podrobnosti týkajúce sa používania úložiska kľúčov v jazyku Java nájdete v našom predchádzajúcom návode.

4. SSL Handshake

Potvrdenia SSL sú mechanizmus, pomocou ktorého klient a server vytvárajú dôveru a logistiku potrebnú na zabezpečenie ich pripojenia cez sieť.

Toto je veľmi zorganizovaný postup a pochopenie podrobností môže pomôcť pochopiť, prečo často zlyháva, čomu sa chceme venovať v nasledujúcej časti.

Typické kroky pri podaní protokolu SSL sú:

  1. Klient poskytne zoznam možných verzií SSL a šifrovacích balíkov, ktoré sa majú použiť
  2. Server súhlasí s konkrétnou verziou SSL a šifrovacím balíkom a odpovie späť svojim certifikátom
  3. Klient extrahuje verejný kľúč z certifikátu a zašle späť zašifrovaný „kľúč pred masterom“
  4. Server dešifruje „kľúč pred aktualizáciou“ pomocou svojho súkromného kľúča
  5. Klient a server vypočítajú „zdieľané tajomstvo“ pomocou vymeneného „kľúča pred master“
  6. Výmena správ medzi klientom a serverom potvrdzujúca úspešné šifrovanie a dešifrovanie pomocou „zdieľaného tajomstva“

Aj keď je väčšina krokov pre každé overenie spojenia SSL rovnaká, medzi jednosmerným a obojsmerným SSL je nepatrný rozdiel. Poďme rýchlo preskúmať tieto rozdiely.

4.1. Handshake v jednosmernom SSL

Ak sa odvolávame na kroky uvedené vyššie, v druhom kroku sa uvádza výmena certifikátov. Jednosmerný protokol SSL vyžaduje, aby klient mohol dôverovať serveru prostredníctvom jeho verejného certifikátu. Toto ponecháva serveru dôverovať všetkým klientom ktoré požadujú spojenie. Server nemôže žiadať a overovať verejný certifikát od klientov, čo môže predstavovať bezpečnostné riziko.

4.2. Handshake v obojsmernom SSL

Pri jednosmernom SSL musí server dôverovať všetkým klientom. Obojsmerné SSL však dodáva serveru schopnosť, aby mohol vytvárať aj dôveryhodných klientov. Počas obojsmerného podania ruky klient aj server sa musia navzájom prezentovať a akceptovať verejné certifikáty pred vytvorením úspešného spojenia.

5. Scenáre zlyhania spojenia

Po vykonaní tejto rýchlej kontroly sa môžeme na scenáre zlyhania pozerať jasnejšie.

Potvrdenie spojenia SSL v jednosmernej alebo obojsmernej komunikácii môže zlyhať z viacerých dôvodov. Prejdeme si každý z týchto dôvodov, simulujeme zlyhanie a pochopíme, ako sa môžeme vyhnúť takýmto scenárom.

V každom z týchto scenárov použijeme SimpleClient a SimpleServer sme vytvorili skôr.

5.1. Chýba certifikát servera

Skúsme spustiť SimpleServer a pripojte ho cez SimpleClient. Aj keď očakávame, že sa zobrazí správa „Hello World!“, Zobrazí sa nám výnimka:

Výnimka vo vlákne „main“ javax.net.ssl.SSLHandshakeException: Bola prijatá fatálna výstraha: handshake_failure

Teraz to naznačuje, že sa niečo pokazilo. The SSLHandshakeException vyššie, abstraktným spôsobom, uvádza, že klient pri pripájaní k serveru nedostal žiadny certifikát.

Na riešenie tohto problému použijeme úložisko kľúčov, ktoré sme vygenerovali skôr, a to tak, že ich odovzdáme ako systémové vlastnosti serveru:

-Djavax.net.ssl.keyStore = clientkeystore.jks -Djavax.net.ssl.keyStorePassword = heslo

Je dôležité poznamenať, že systémová vlastnosť pre cestu k súboru úložiska kľúčov by mala byť buď absolútna cesta, alebo by mal byť súbor úložiska kľúčov umiestnený v rovnakom adresári, z ktorého je vyvolaný príkaz Java na spustenie servera. Systémová vlastnosť Java pre sklad kľúčov nepodporuje relatívne cesty.

Pomáha nám to dosiahnuť výstup, ktorý očakávame? Dozvieme sa to v nasledujúcej podkapitole.

5.2. Nedôveryhodný certifikát servera

Ako bežíme SimpleServer a SimpleClient opäť so zmenami v predchádzajúcej podkapitole, čo dostaneme ako výstup:

Výnimka vo vlákne „main“ javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: Budovanie cesty PKIX zlyhalo: sun.security.provider.certpath.SunCertPathBuilderException: nie je možné nájsť platnú certifikačnú cestu k požadovanému cieľu

Nefungovalo to presne podľa našich očakávaní, ale vyzerá to, že zlyhalo z iného dôvodu.

Toto konkrétne zlyhanie je spôsobené skutočnosťou, že náš server používa a podpísaný sám sebou certifikát, ktorý nie je podpísaný certifikačnou autoritou (CA).

Naozaj sa táto chyba zobrazí vždy, keď je certifikát podpísaný niečím iným, ako je v predvolenom úložisku dôvery. Predvolené úložisko dôveryhodností v JDK sa zvyčajne dodáva s informáciami o používaných bežných CA.

Aby sme tu mohli vyriešiť tento problém, budeme musieť prinútiť SimpleClient dôverovať certifikátu predloženému SimpleServer. Využime úložisko dôvery, ktoré sme vygenerovali skôr, keď ho klientovi odovzdáme ako vlastnosti systému:

-Djavax.net.ssl.trustStore = clienttruststore.jks -Djavax.net.ssl.trustStorePassword = heslo

Upozorňujeme, že to nie je ideálne riešenie. V ideálnom prípade by sme nemali používať certifikát podpísaný sám sebou, ale certifikát, ktorý bol certifikovaný certifikačnou autoritou (CA), ktorej môžu klienti predvolene dôverovať.

Poďme na ďalšiu podsekciu a zistíme, či teraz dostaneme náš očakávaný výstup.

5.3. Chýba certifikát klienta

Skúsme ešte raz spustiť SimpleServer a SimpleClient po uplatnení zmien z predchádzajúcich pododdielov:

Výnimka vo vlákne „main“ java.net.SocketException: Softvér spôsobil prerušenie pripojenia: recv zlyhal

Opäť nie niečo, čo sme očakávali. The SocketException tu nám hovorí, že server nemohol dôverovať klientovi. Je to tak preto, lebo sme nastavili obojsmerné SSL. V našom SimpleServer máme:

((SSLServerSocket) poslucháč) .setNeedClientAuth (true);

Vyššie uvedený kód označuje SSLServerSocket sa vyžaduje na autentifikáciu klienta prostredníctvom jeho verejného certifikátu.

Môžeme vytvoriť úložisko kľúčov pre klienta a zodpovedajúce úložisko dôveryhodnosti pre server podobným spôsobom, aký sme použili pri vytváraní predchádzajúceho úložiska kľúčov a úložiska dôvery.

Reštartujeme server a odovzdáme mu nasledujúce vlastnosti systému:

-Djavax.net.ssl.keyStore = serverkeystore.jks \ -Djavax.net.ssl.keyStorePassword = heslo \ -Djavax.net.ssl.trustStore = servertruststore.jks \ -Djavax.net.ssl.trustStorePassword = heslo

Potom klienta reštartujeme odovzdaním týchto vlastností systému:

-Djavax.net.ssl.keyStore = clientkeystore.jks \ -Djavax.net.ssl.keyStorePassword = heslo \ -Djavax.net.ssl.trustStore = clienttruststore.jks \ -Djavax.net.ssl.trustStorePassword = heslo

Nakoniec máme požadovaný výstup:

Ahoj Svet!

5.4. Nesprávne certifikáty

Okrem vyššie uvedených chýb môže dôjsť k zlyhaniu spojenia z rôznych dôvodov týkajúcich sa spôsobu, akým sme vytvorili certifikáty. Jedna častá chyba súvisí s nesprávnym KN. Poďme preskúmať podrobnosti skladu kľúčov servera, ktorý sme vytvorili predtým:

keytool -v -list -keystore serverkeystore.jks

Keď spustíme vyššie uvedený príkaz, môžeme vidieť podrobnosti o sklade kľúčov, konkrétne o vlastníkovi:

... Vlastník: CN = localhost, OU = technológia, O = baeldung, L = mesto, ST = štát, C = xx ...

CN vlastníka tohto certifikátu je nastavený na localhost. CN vlastníka sa musí presne zhodovať s hostiteľom servera. Ak dôjde k nesúladu, bude mať za následok SSLHandshakeException.

Pokúsme sa vygenerovať certifikát servera s CN na čokoľvek iné ako localhost. Keď teraz použijeme regenerovaný certifikát na spustenie SimpleServer a SimpleClient okamžite zlyhá:

Výnimka vo vlákne „main“ javax.net.ssl.SSLHandshakeException: java.security.cert.CertificateException: Nenašiel sa žiadny názov zodpovedajúci localhost

Vyššie uvedená stopa výnimky jasne naznačuje, že klient očakával certifikát s menom ako localhost, ktorý nenašiel.

Vezmite prosím na vedomie, že JSSE predvolene nenariaďuje overenie názvu hostiteľa. V. Sme povolili overenie názvu hostiteľa SimpleClient prostredníctvom explicitného použitia HTTPS:

SSLParameters sslParams = nové SSLParameters (); sslParams.setEndpointIdentificationAlgorithm ("HTTPS"); ((SSLSocket) pripojenie) .setSSLParameters (sslParams);

Overenie názvu hostiteľa je častou príčinou zlyhania a vo všeobecnosti by sa malo vynútiť kvôli lepšej bezpečnosti. Podrobnosti o overení názvu hostiteľa a jeho dôležitosti v zabezpečení pomocou protokolu TLS nájdete v tomto článku.

5.5. Nekompatibilná verzia SSL

V súčasnosti fungujú rôzne kryptografické protokoly vrátane rôznych verzií SSL a TLS.

Ako už bolo spomenuté, protokol SSL bol vo všeobecnosti nahradený protokolom TLS kvôli svojej kryptografickej sile. Kryptografický protokol a verzia sú ďalším prvkom, na ktorom sa klient a server musia počas nadviazania spojenia dohodnúť.

Napríklad, ak server používa kryptografický protokol SSL3 a klient používa TLS1.3, nemôže sa dohodnúť na kryptografickom protokole a protokole SSLHandshakeException bude vygenerovaný.

V našom SimpleClient zmeňme protokol na niečo, čo nie je kompatibilné s protokolom nastaveným pre server:

((SSLSocket) pripojenie) .setEnabledProtocols (nový reťazec [] {"TLSv1.1"});

Keď znova spustíme nášho klienta, dostaneme SSLHandshakeException:

Výnimka vo vlákne „main“ javax.net.ssl.SSLHandshakeException: Žiadny vhodný protokol (protokol je zakázaný alebo šifrovacie sady sú nevhodné)

Stopa výnimky je v takýchto prípadoch abstraktná a nehovorí nám presný problém. Na vyriešenie týchto typov problémov je potrebné overiť, či klient aj server používajú rovnaké alebo kompatibilné kryptografické protokoly.

5.6. Nekompatibilná šifrovacia sada

Klient a server sa tiež musia dohodnúť na šifrovacej sade, ktorú použijú na šifrovanie správ.

Počas nadviazania spojenia klient predstaví zoznam možných šifier, ktoré je možné použiť, a server odpovie vybranou šifrou zo zoznamu. Server vygeneruje SSLHandshakeException ak nemôže zvoliť vhodnú šifru.

V našom SimpleClient poďme zmeniť šifrovaciu sadu na niečo, čo nie je kompatibilné so šifrovacou sadou používanou našim serverom:

((SSLSocket) pripojenie) .setEnabledCipherSuites (nový reťazec [] {"TLS_RSA_WITH_AES_128_GCM_SHA256"});

Keď reštartujeme nášho klienta, dostaneme SSLHandshakeException:

Výnimka vo vlákne „main“ javax.net.ssl.SSLHandshakeException: Dostalo sa fatálne upozornenie: handshake_failure

Stopa výnimky je opäť dosť abstraktná a nehovorí nám presný problém. Riešením tejto chyby je overenie povolených šifrovacích balíkov používaných klientom aj serverom a zabezpečenie dostupnosti najmenej jednej spoločnej sady.

Za normálnych okolností sú klienti a servery nakonfigurované tak, aby používali širokú škálu šifrovacích balíkov, takže je menej pravdepodobné, že k tejto chybe dôjde. Ak narazíme na túto chybu, je to zvyčajne preto, lebo server bol nakonfigurovaný na použitie veľmi selektívnej šifry. Server sa môže z bezpečnostných dôvodov rozhodnúť vynútiť výberovú sadu šifier.

6. Záver

V tomto tutoriáli sme sa dozvedeli o nastavení SSL pomocou zásuviek Java. Potom sme diskutovali o SSL spojeniach s jednosmerným a obojsmerným SSL. Na záver sme prešli zoznamom možných dôvodov, prečo môže zlyhať overenie spojenia SSL, a diskutovali sme o riešeniach.

Ako vždy, kód príkladov je k dispozícii na GitHub.

Java dole

Práve som oznámil nové Naučte sa jar kurz zameraný na základy jari 5 a Spring Boot 2:

>> SKONTROLUJTE KURZ

$config[zx-auto] not found$config[zx-overlay] not found