SSL kézfogás hibák

Java Top

Most jelentettem be az újat Tanulj tavaszt tanfolyam, amelynek középpontjában az 5. tavasz és a tavaszi bakancs 2 alapjai állnak:

>> ELLENŐRIZZE A FOLYAMATOT

1. Áttekintés

A Secured Socket Layer (SSL) egy kriptográfiai protokoll, amely biztonságot nyújt a hálózaton keresztüli kommunikációban. Ebben az oktatóanyagban különféle forgatókönyveket vitatunk meg, amelyek SSL-kézfogás meghibásodását eredményezhetik, és ennek mikéntjét.

Ne feledje, hogy a JSSE-t használó SSL-bevezetésünk részletesebben átfogja az SSL alapjait.

2. Terminológia

Fontos megjegyezni, hogy a biztonsági rések miatt az SSL szabványt a Transport Layer Security (TLS) váltja fel. A legtöbb programozási nyelv, beleértve a Java-t is, rendelkezik könyvtárakkal, amelyek támogatják az SSL-t és a TLS-t is.

Az SSL kezdete óta számos termék és nyelv, például az OpenSSL és a Java hivatkozott az SSL-re, amelyet a TLS átvétele után is megtartottak. Ezért az oktatóanyag további részében az SSL kifejezést fogjuk használni, hogy általában a kriptográfiai protokollokra utaljunk.

3. Beállítás

Az oktatóanyag céljából létrehozunk egy egyszerű szerver- és kliensalkalmazást a Java Socket API segítségével a hálózati kapcsolat szimulálására.

3.1. Kliens és szerver létrehozása

A Java-ban használhatjuk scsatlakozók kommunikációs csatorna létrehozására a szerver és az ügyfél között a hálózaton keresztül. A foglalatok a Java Secure Socket Extension (JSSE) részét képezik.

Kezdjük egy egyszerű szerver definiálásával:

int port = 8443; ServerSocketFactory gyár = SSLServerSocketFactory.getDefault (); próbáld ki (ServerSocket figyelő = gyár.createServerSocket (port)) {SSLServerSocket sslListener = (SSLServerSocket) figyelő; sslListener.setNeedClientAuth (true); sslListener.setEnabledCipherSuites (új karakterlánc [] {"TLS_DHE_DSS_WITH_AES_256_CBC_SHA256"}); sslListener.setEnabledProtocols (új karakterlánc [] {"TLSv1.2"}); while (true) {try (Socket socket = sslListener.accept ()) {PrintWriter out = new PrintWriter (socket.getOutputStream (), true); out.println ("Helló világ!"); }}}

A fent definiált szerver a „Hello World!” Üzenetet adja vissza csatlakoztatott kliensnek.

Ezután határozzunk meg egy alap klienst, amelyet összekapcsolunk az ügyfelünkkel SimpleServer:

String host = "localhost"; int port = 8443; SocketFactory gyár = SSLSocketFactory.getDefault (); próbálkozzon (Socket kapcsolat = gyár.createSocket (gazdagép, port)) {((SSLSocket) kapcsolat) .setEnabledCipherSuites (új karakterlánc [] {"TLS_DHE_DSS_WITH_AES_256_CBC_SHA256"}); ((SSLSocket) kapcsolat) .setEnabledProtocols (új karakterlánc [] {"TLSv1.2"}); SSLParameters sslParams = new SSLParameters (); sslParams.setEndpointIdentificationAlgorithm ("HTTPS"); ((SSLSocket) kapcsolat) .setSSLParameters (sslParams); BufferedReader input = new BufferedReader (új InputStreamReader (connection.getInputStream ())); return input.readLine (); }

Ügyfelünk kinyomtatja a szerver által visszaküldött üzenetet.

3.2. Tanúsítványok létrehozása Java-ban

Az SSL titoktartást, integritást és hitelességet biztosít a hálózati kommunikációban. A tanúsítványok fontos szerepet játszanak a hitelesség megállapításában.

Ezeket a tanúsítványokat általában egy tanúsító hatóság vásárolja meg és írja alá, de ehhez az oktatóanyaghoz önaláírt tanúsítványokat fogunk használni.

Ennek elérésére felhasználhatjuk kulcseszköz, amely a JDK-val szállít:

$ keytool -genkey -keypass jelszó \ -storepass jelszó \ -keystore serverkeystore.jks

A fenti parancs interaktív héjat indít a tanúsítványhoz szükséges információk összegyűjtésére, mint például a Common Name (CN) és a Distinguished Name (DN). Amikor minden lényeges adatot megadunk, akkor létrehozza a fájlt serverkeystore.jks, amely tartalmazza a szerver magánkulcsát és nyilvános tanúsítványát.

Vegye figyelembe, hogy serverkeystore.jks a Java kulcstár (JKS) formátumban van tárolva, amely a Java tulajdonosa. Ezek a napok, kulcstartó emlékeztetni fog minket, hogy fontolóra kell vennünk a PKCS # 12 használatát, amelyet szintén támogat.

Használhatjuk tovább kulcstartó a nyilvános tanúsítvány kibontása a létrehozott kulcstárfájlból:

$ keytool -export -storepass jelszó \ -fájl server.cer \ -keystore serverkeystore.jks

A fenti parancs fájlként exportálja a nyilvános tanúsítványt a kulcstárból szerver.cer. Használjuk az exportált tanúsítványt az ügyfél számára úgy, hogy hozzáadjuk a megbízhatósági üzletéhez:

$ keytool -import -v -trustcacerts \ -fájlkiszolgáló.cer \ -keypass jelszó \ -storepass jelszó \ -keystore clienttruststore.jks

Most létrehoztunk egy kulcstárat a kiszolgálóhoz és az ehhez tartozó megbízhatósági üzletet az ügyfél számára. Majd áttekintjük ezen generált fájlok használatát, amikor megbeszéljük az esetleges kézfogási hibákat.

A Java kulcstárának használatáról további részletek találhatók korábbi oktatóanyagunkban.

4. SSL kézfogás

Az SSL kézfogások egy olyan mechanizmus, amellyel az ügyfél és a szerver megteremti a hálózaton keresztüli kapcsolatuk biztosításához szükséges bizalmat és logisztikát.

Ez egy nagyon hangszerelt eljárás, és ennek részleteinek megértése segíthet megérteni, hogy miért nem sikerül gyakran, amelyet a következő szakaszban szándékozunk bemutatni.

Az SSL kézfogás tipikus lépései:

  1. Az ügyfél megadja a lehetséges SSL verziók és titkosítócsomagok listáját
  2. A szerver megállapodik egy adott SSL verzióban és titkosító csomagban, válaszolva a tanúsítvánnyal
  3. Az ügyfél kivonja a nyilvános kulcsot a tanúsítványból, és egy titkosított „pre-master key” -vel válaszol vissza.
  4. A kiszolgáló titkos kulcsával visszafejti az „elő-master kulcsot”
  5. Az ügyfél és a kiszolgáló kiszámítja a „megosztott titkot” a kicserélt „pre-master kulcs” segítségével
  6. A sikeres titkosítást és visszafejtést megerősítő kliens és szerver üzeneteket cserél a „megosztott titok” segítségével

Bár a lépések többsége megegyezik bármely SSL-kézfogással, finom különbség van az egyirányú és a kétirányú SSL között. Nézzük át gyorsan ezeket a különbségeket.

4.1. A kézfogás egyirányú SSL-ben

Ha a fent említett lépésekre hivatkozunk, a második lépés megemlíti a tanúsítványcserét. Az egyirányú SSL megköveteli, hogy az ügyfél nyilvános tanúsítványa révén megbízhasson a szerverben. Ez elhagyja a szervert, hogy megbízzon az összes kliensben amelyek kapcsolatot kérnek. A kiszolgálónak nincs módja a nyilvános tanúsítvány kérését és érvényesítését az ügyfelektől, ami biztonsági kockázatot jelenthet.

4.2. A kézfogás kétirányú SSL-ben

Egyirányú SSL esetén a szervernek megbíznia kell minden ügyfélben. De a kétirányú SSL növeli a szerver képességét arra, hogy megbízható klienseket is létrehozhasson. Kétirányú kézfogás közben mind az ügyfélnek, mind a szervernek be kell mutatnia és el kell fogadnia egymás nyilvános tanúsítványait mielőtt sikeres kapcsolat létrejöhet.

5. Kézfogás meghiúsulási forgatókönyvek

Miután elvégezte ezt a gyors áttekintést, nagyobb egyértelműséggel tekinthetünk meg a meghibásodási forgatókönyvekre.

Az SSL kézfogás egy- vagy kétirányú kommunikációban több okból is meghiúsulhat. Végig fogjuk járni ezeket az okokat, szimuláljuk a kudarcot és megértjük, hogyan kerülhetjük el az ilyen forgatókönyveket.

Ezen forgatókönyvek mindegyikében a SimpleClient és SimpleServer korábban hoztunk létre.

5.1. Hiányzik a szerver tanúsítvány

Próbáljuk meg futtatni a SimpleServer és csatlakoztassa a SimpleClient. Bár várhatóan a „Hello World!” Üzenet jelenik meg, kivételünk van:

Kivétel a "main" szálban javax.net.ssl.SSLHandshakeException: Fatal figyelmeztetés érkezett: handshake_failure

Ez azt jelzi, hogy valami elromlott. A SSLHandshakeException absztrakt módon, azt állítja, hogy az ügyfél a szerverhez való csatlakozáskor nem kapott tanúsítványt.

A probléma megoldásához a korábban létrehozott kulcstárolót fogjuk használni azzal, hogy rendszer tulajdonságként továbbítjuk őket a szerverre:

-Djavax.net.ssl.keyStore = clientkeystore.jks -Djavax.net.ssl.keyStorePassword = jelszó

Fontos megjegyezni, hogy a kulcstárfájl elérési útjának rendszertulajdonságának abszolút elérési útnak kell lennie, vagy a kulcstárfájlt ugyanabba a könyvtárba kell helyezni, ahonnan a kiszolgáló indításához a Java parancsot hívják meg. A kulcstároló Java rendszer tulajdonságai nem támogatják a relatív elérési utakat.

Segít ez abban, hogy a várt eredményt megszerezzük? Tudjuk meg a következő alszakaszban.

5.2. Nem megbízható szerver tanúsítvány

Ahogy futunk a SimpleServer és a SimpleClient megint az előző alszakasz változásával mit kapunk kimenetként:

Kivétel a "main" szálban

Nos, nem pontosan úgy működött, mint amire számítottunk, de úgy tűnik, hogy más okból nem sikerült.

Ezt a bizonyos hibát az okozza, hogy szerverünk a saját aláírással tanúsítvány, amelyet nem tanúsító hatóság (CA) írt alá.

Valóban, amikor a tanúsítványt aláírja valami más, mint ami az alapértelmezett truststore-ban található, látni fogjuk ezt a hibát. A JDK alapértelmezett megbízható áruháza általában információkat tartalmaz a használatban lévő általános hitelesítésszolgáltatókról.

Hogy ezt a kérdést itt megoldhassuk, kényszerítenünk kell SimpleClient hogy megbízzon a bemutatott igazolásban SimpleServer. Használjuk a korábban létrehozott bizalomüzletet azzal, hogy rendszer tulajdonságként továbbítjuk őket az ügyfélnek:

-Djavax.net.ssl.trustStore = clienttruststore.jks -Djavax.net.ssl.trustStorePassword = jelszó

Felhívjuk figyelmét, hogy ez nem ideális megoldás. Ideális esetben nem önaláírt tanúsítványt kell használnunk, hanem egy tanúsító hatóság által hitelesített tanúsítványt, amelyben az ügyfelek alapértelmezés szerint megbízhatnak.

Menjünk a következő alszakaszra, hogy megtudjuk, megkapjuk-e most a várt kimenetelünket.

5.3. Hiányzik az ügyféltanúsítvány

Próbálkozzunk még egyszer a SimpleServer és a SimpleClient futtatásával, az előző alszakaszok módosításainak alkalmazásával:

Kivétel a "main" szálban java.net.SocketException: A szoftver okozta a kapcsolat megszakítását: a recv nem sikerült

Megint nem olyasmi, amire számítottunk. A SocketException itt azt mondja nekünk, hogy a szerver nem tudott megbízni az ügyfélben. Ennek oka az, hogy kétirányú SSL-t hoztunk létre. Miénkben SimpleServer nekünk van:

((SSLServerSocket) figyelő) .setNeedClientAuth (true);

A fenti kód egy SSLServerSocket szükséges a kliens hitelesítéséhez nyilvános tanúsítványuk révén.

Hozhatunk létre kulcstárat az ügyfél számára, és egy megfelelő megbízható tárolót a kiszolgálóhoz hasonló módon, mint amit az előző kulcstár és megbízható áruház létrehozásakor használtunk.

Indítjuk újra a szervert, és átadjuk neki a következő rendszer tulajdonságokat:

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

Ezután a rendszer tulajdonságainak átadásával újraindítjuk az ügyfelet:

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

Végül megvan a kívánt kimenet:

Helló Világ!

5.4. Helytelen tanúsítványok

A fenti hibákon kívül a kézfogás meghiúsulhat a tanúsítványok létrehozásának módjával kapcsolatos különféle okok miatt. Az egyik gyakori hiba a hibás CN-hez kapcsolódik. Fedezzük fel a korábban létrehozott szerver kulcstár részleteit:

keytool -v -list -keystore serverkeystore.jks

A fenti parancs futtatásakor láthatjuk a kulcstár részleteit, nevezetesen a tulajdonosát:

... Tulajdonos: CN = localhost, OU = technológia, O = baeldung, L = város, ST = állapot, C = xx ...

A tanúsítvány tulajdonosának CN-je localhost-ra van állítva. A tulajdonos CN-jének pontosan meg kell egyeznie a szerver gazdagépével. Ha bármilyen eltérés van, akkor egy SSLHandshakeException.

Próbáljuk meg újratermeszteni a szerver tanúsítványt a CN-vel, mint bármi más, mint a localhost. Amikor a regenerált tanúsítványt most használjuk a SimpleServer és SimpleClient azonnal meghiúsul:

Kivétel a "main" szálban javax.net.ssl.SSLHandshakeException: java.security.cert.CertificateException: Nem található a helyi hostnak megfelelő név

A fenti kivétel nyom egyértelműen jelzi, hogy az ügyfél egy localhost névvel ellátott tanúsítványra számított, amelyet nem talált meg.

Kérjük, vegye figyelembe, hogy A JSSE alapértelmezés szerint nem írja elő a hosztnév ellenőrzését. Engedélyeztük a hosztnév ellenőrzését a SimpleClient a HTTPS kifejezett használatával:

SSLParameters sslParams = new SSLParameters (); sslParams.setEndpointIdentificationAlgorithm ("HTTPS"); ((SSLSocket) kapcsolat) .setSSLParameters (sslParams);

A gazdagépnév ellenőrzése a hiba gyakori oka, és általában a nagyobb biztonság érdekében mindig érvényesíteni kell. A gazdagépnév ellenőrzéséről és annak fontosságáról a TLS biztonságában olvassa el ezt a cikket.

5.5. Inkompatibilis SSL verzió

Jelenleg különféle kriptográfiai protokollok vannak, köztük az SSL és a TLS különböző verziói.

Mint korábban említettük, az SSL-t általában a TLS helyettesítette kriptográfiai ereje miatt. A kriptográfiai protokoll és a verzió további elem, amelyben az ügyfélnek és a szervernek meg kell egyeznie a kézfogás során.

Például, ha a kiszolgáló SSL3 titkosítási protokollt használ, és az ügyfél TLS1.3-at használ, akkor nem tudnak megállapodni egy kriptográfiai protokollban és egy SSLHandshakeException generálódik.

Miénkben SimpleClient cseréljük a protokollt valamire, ami nem kompatibilis a kiszolgálón beállított protokollal:

((SSLSocket) kapcsolat) .setEnabledProtocols (új karakterlánc [] {"TLSv1.1"});

Amikor újra futtatjuk ügyfelünket, kapunk egy SSLHandshakeException:

Kivétel a "main" szálban javax.net.ssl.SSLHandshakeException: Nincs megfelelő protokoll (a protokoll le van tiltva, vagy a titkosítócsomagok nem megfelelőek)

A kivétel nyom ilyen esetekben elvont, és nem mondja el a pontos problémát. Az ilyen típusú problémák megoldásához ellenőrizni kell, hogy mind az ügyfél, mind a kiszolgáló ugyanazokat vagy kompatibilis titkosítási protokollokat használja-e.

5.6. Inkompatibilis Cipher Suite

Az ügyfélnek és a kiszolgálónak meg kell állapodnia az üzenetek titkosításához használt titkosító csomagban is.

A kézfogás során az ügyfél bemutatja a lehetséges titkosítások listáját, és a kiszolgáló válaszol a listából kiválasztott titkosítással. A szerver létrehoz egy SSLHandshakeException ha nem tudja kiválasztani a megfelelő rejtjelet.

Miénkben SimpleClient változtassuk meg a titkosító csomagot olyanra, amely nem kompatibilis a szerverünk által használt titkosító csomaggal:

(((SSLSocket) kapcsolat) .setEnabledCipherSuites (új karakterlánc [] {"TLS_RSA_WITH_AES_128_GCM_SHA256"});

Amikor újraindítjuk ügyfelünket, kapunk egy SSLHandshakeException:

Kivétel a "main" szálban javax.net.ssl.SSLHandshakeException: Fatal figyelmeztetés érkezett: handshake_failure

Ismételten, a kivétel nyoma meglehetősen elvont, és nem mondja el a pontos problémát. Az ilyen hiba megoldása az ügyfél és a szerver által használt engedélyezett titkosító csomagok ellenőrzése, és annak biztosítása, hogy rendelkezésre áll legalább egy közös csomag.

Normális esetben az ügyfelek és a kiszolgálók úgy vannak konfigurálva, hogy sokféle titkosító csomagot használjanak, így ez a hiba kevésbé valószínű. Ha találkozunk ezzel a hibával, ez általában azért van, mert a kiszolgálót úgy állították be, hogy nagyon szelektív titkosítást használjon. A szerver biztonsági okokból választhatja a szelektív kódkészlet kényszerítését.

6. Következtetés

Ebben az oktatóanyagban megtanultuk az SSL Java socket használatával történő beállítását. Ezután megvitattuk az SSL kézfogásokat egy- és kétirányú SSL-lel. Végül áttekintettük azokat a lehetséges okokat, amelyek miatt az SSL kézfogások meghiúsulhatnak, és megvitattuk a megoldásokat.

Mint mindig, a példák kódja elérhető a GitHub oldalon.

Java alsó

Most jelentettem be az újat Tanulj tavaszt tanfolyam, amelynek középpontjában az 5. tavasz és a tavaszi bakancs 2 alapjai állnak:

>> ELLENŐRIZZE A FOLYAMATOT

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