SSL-håndtrykkingsfeil

Java Top

Jeg kunngjorde nettopp det nye Lær våren kurs, med fokus på det grunnleggende i vår 5 og vårstøvel 2:

>> KONTROLLER KURSET

1. Oversikt

Secured Socket Layer (SSL) er en kryptografisk protokoll som gir sikkerhet i kommunikasjon over nettverket. I denne opplæringen vil vi diskutere forskjellige scenarier som kan resultere i en SSL-håndtrykkingsfeil og hvordan du gjør det.

Merk at vår introduksjon til SSL ved hjelp av JSSE dekker det grunnleggende om SSL mer detaljert.

2. Terminologi

Det er viktig å merke seg at SSL på grunn av sikkerhetsproblemer erstattes av Transport Layer Security (TLS). De fleste programmeringsspråk, inkludert Java, har biblioteker som støtter både SSL og TLS.

Siden begynnelsen av SSL hadde mange produkter og språk som OpenSSL og Java referanser til SSL som de beholdt selv etter at TLS overtok. Av denne grunn vil vi resten av denne opplæringen bruke begrepet SSL for å referere generelt til kryptografiske protokoller.

3. Oppsett

For formålet med denne opplæringen oppretter vi en enkel server og klientapplikasjoner ved hjelp av Java Socket API for å simulere en nettverkstilkobling.

3.1. Opprette en klient og en server

I Java kan vi bruke sockets å etablere en kommunikasjonskanal mellom en server og klient over nettverket. Stikkontakter er en del av Java Secure Socket Extension (JSSE) i Java.

La oss begynne med å definere en enkel server:

int port = 8443; ServerSocketFactory fabrikk = SSLServerSocketFactory.getDefault (); prøv (ServerSocket lytter = factory.createServerSocket (port)) {SSLServerSocket sslListener = (SSLServerSocket) lytter; sslListener.setNeedClientAuth (true); sslListener.setEnabledCipherSuites (ny streng [] {"TLS_DHE_DSS_WITH_AES_256_CBC_SHA256"}); sslListener.setEnabledProtocols (ny streng [] {"TLSv1.2"}); while (true) {try (Socket socket = sslListener.accept ()) {PrintWriter out = new PrintWriter (socket.getOutputStream (), true); out.println ("Hello World!"); }}}

Serveren som er definert ovenfor, returnerer meldingen "Hello World!" til en tilkoblet klient.

La oss deretter definere en grunnleggende klient som vi kobler til vår SimpleServer:

Strengvert = "localhost"; int port = 8443; SocketFactory fabrikk = SSLSocketFactory.getDefault (); prøv (Socket connection = factory.createSocket (host, port)) {((SSLSocket) connection) .setEnabledCipherSuites (new String [] {"TLS_DHE_DSS_WITH_AES_256_CBC_SHA256"}); ((SSLSocket) tilkobling) .setEnabledProtocols (ny streng [] {"TLSv1.2"}); SSLParameters sslParams = nye SSLParameters (); sslParams.setEndpointIdentificationAlgorithm ("HTTPS"); ((SSLSocket) tilkobling) .setSSLParameters (sslParams); BufferedReader input = new BufferedReader (new InputStreamReader (connection.getInputStream ())); return input.readLine (); }

Klienten vår skriver ut meldingen som er returnert av serveren.

3.2. Opprette sertifikater i Java

SSL gir hemmelighold, integritet og ekthet i nettverkskommunikasjon. Sertifikater spiller en viktig rolle så langt som å etablere ekthet.

Vanligvis kjøpes og signeres disse sertifikatene av en sertifikatmyndighet, men for denne opplæringen bruker vi selvsignerte sertifikater.

For å oppnå dette kan vi bruke nøkkelverktøy, som sendes med JDK:

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

Ovennevnte kommando starter et interaktivt skall for å samle informasjon for sertifikatet som Common Name (CN) og Distinguished Name (DN). Når vi gir alle relevante detaljer, genererer den filen serverkeystore.jks, som inneholder serverens private nøkkel og dets offentlige sertifikat.

Noter det serverkeystore.jks lagres i Java Key Store (JKS) -formatet, som er proprietært til Java. Disse dager, nøkkelverktøy vil minne oss om at vi burde vurdere å bruke PKCS # 12, som den også støtter.

Vi kan bruke den videre nøkkelverktøy for å trekke ut det offentlige sertifikatet fra den genererte keystore-filen:

$ keytool -export -storepass passord \ -fil server.cer \ -keystore serverkeystore.jks

Ovennevnte kommando eksporterer det offentlige sertifikatet fra keystore som en fil server.cer. La oss bruke det eksporterte sertifikatet for klienten ved å legge det til tillitsbutikken:

$ keytool -import -v -trustcacerts \ -fil server.cer \ -keypass passord \ -storepass passord \ -keystore clienttruststore.jks

Vi har nå generert en keystore for serveren og tilsvarende truststore for klienten. Vi vil gå gjennom bruken av disse genererte filene når vi diskuterer mulige feil i håndtrykk.

Og mer informasjon om bruken av Java's keystore finner du i vår forrige opplæring.

4. SSL-håndtrykk

SSL-håndtrykk er en mekanisme der en klient og server etablerer den tilliten og logistikken som kreves for å sikre forbindelsen via nettverket.

Dette er en veldig orkestrert prosedyre, og å forstå detaljene i dette kan hjelpe deg med å forstå hvorfor det ofte mislykkes, noe vi har tenkt å dekke i neste avsnitt.

Typiske trinn i et SSL-håndtrykk er:

  1. Klienten gir en liste over mulige SSL-versjoner og krypteringsserier som skal brukes
  2. Serveren er enig i en bestemt SSL-versjon og krypteringspakke, og svarer tilbake med sertifikatet
  3. Klienten trekker ut den offentlige nøkkelen fra sertifikatet, svarer tilbake med en kryptert “pre-master key”
  4. Server dekrypterer "pre-master key" ved hjelp av den private nøkkelen
  5. Klient og server beregner en “delt hemmelighet” ved hjelp av den utvekslede “pre-master key”
  6. Klient- og serverutvekslingsmeldinger som bekrefter vellykket kryptering og dekryptering ved hjelp av den "delte hemmeligheten"

Mens de fleste trinnene er de samme for ethvert SSL-håndtrykk, er det en subtil forskjell mellom enveis og toveis SSL. La oss raskt gjennomgå disse forskjellene.

4.1. Håndtrykket i enveis SSL

Hvis vi refererer til trinnene nevnt ovenfor, nevner trinn to sertifikatutvekslingen. Enveis SSL krever at en klient kan stole på serveren gjennom sitt offentlige sertifikat. Dette forlater serveren å stole på alle klienter som ber om tilkobling. Det er ingen måte for en server å be om og validere det offentlige sertifikatet fra klienter som kan utgjøre en sikkerhetsrisiko.

4.2. Håndtrykket i toveis SSL

Med enveis SSL må serveren stole på alle klienter. Men toveis SSL legger til at serveren også kan etablere pålitelige klienter. Under et toveis håndtrykk, både klient og server må presentere og godta hverandres offentlige sertifikater før en vellykket forbindelse kan opprettes.

5. Scenarier for feil ved håndtrykk

Etter å ha gjort den raske gjennomgangen, kan vi se på feilscenarier med større klarhet.

Et SSL-håndtrykk, i en- eller toveiskommunikasjon, kan mislykkes av flere grunner. Vi vil gå gjennom hver av disse grunnene, simulere feilen og forstå hvordan vi kan unngå slike scenarier.

I hvert av disse scenariene vil vi bruke SimpleClient og SimpleServer vi opprettet tidligere.

5.1. Manglende serversertifikat

La oss prøve å kjøre SimpleServer og koble den gjennom SimpleClient. Mens vi forventer å se meldingen "Hello World!", Får vi et unntak:

Unntak i tråden "hoved" javax.net.ssl.SSLHandshakeException: Mottatt dødelig varsel: handshake_failure

Dette indikerer at noe gikk galt. De SSLHandshakeException ovenfor, på en abstrakt måte, sier at klienten ikke fikk noe sertifikat ved tilkobling til serveren.

For å løse dette problemet vil vi bruke nøkkelbutikken vi genererte tidligere ved å sende dem som systemegenskaper til serveren:

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

Det er viktig å merke seg at systemegenskapen for nøkkelbutikkfilbanen enten skal være en absolutt bane, eller at nøkkellagerfilen skal plasseres i samme katalog der Java-kommandoen påkalles for å starte serveren. Java-systemegenskap for keystore støtter ikke relative stier.

Hjelper dette oss med å få den produksjonen vi forventer? La oss finne ut av det i neste underavsnitt.

5.2. Utrovert serversertifikat

Når vi kjører SimpleServer og SimpleClient igjen med endringene i forrige underseksjon, hva får vi som utgang:

Unntak i tråden "main" javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: kunne ikke finne gyldig sertifiseringsbane til ønsket mål

Vel, det fungerte ikke akkurat som vi forventet, men ser ut til at det har mislyktes av en annen grunn.

Denne spesielle feilen skyldes at serveren vår bruker en selvsignert sertifikat som ikke er signert av en Certificate Authority (CA).

Virkelig, hver gang sertifikatet er signert av noe annet enn det som er i standard tillitsbutikk, ser vi denne feilen. Standard tillitsbutikk i JDK sendes vanligvis med informasjon om vanlige CAer som er i bruk.

For å løse dette problemet her, må vi tvinge SimpleClient å stole på sertifikatet presentert av SimpleServer. La oss bruke tillitsbutikken vi genererte tidligere ved å overføre dem som systemegenskaper til klienten:

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

Vær oppmerksom på at dette ikke er en ideell løsning. I et ideelt scenario bør vi ikke bruke et selvsignert sertifikat, men et sertifikat som er sertifisert av en Certificate Authority (CA) som klienter kan stole på som standard.

La oss gå til neste underseksjon for å finne ut om vi får den forventede produksjonen nå.

5.3. Manglende klientsertifikat

La oss prøve en gang til å kjøre SimpleServer og SimpleClient, etter å ha brukt endringene fra forrige underseksjoner:

Unntak i tråden "main" java.net.SocketException: Programvaren forårsaket tilkobling avbrutt: recv mislyktes

Igjen, ikke noe vi forventet. De SocketException her forteller oss at serveren ikke kunne stole på klienten. Dette er fordi vi har satt opp en toveis SSL. I vår SimpleServer vi har:

((SSLServerSocket) lytter) .setNeedClientAuth (true);

Ovennevnte kode indikerer en SSLServerSocket kreves for klientautentisering gjennom deres offentlige sertifikat.

Vi kan opprette en keystore for klienten og en tilsvarende truststore for serveren på en måte som ligner den vi brukte da vi opprettet den forrige keystore og truststore.

Vi starter serveren på nytt og sender den følgende systemegenskaper:

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

Deretter starter vi klienten på nytt ved å sende disse systemegenskapene:

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

Til slutt har vi ønsket produksjon:

Hei Verden!

5.4. Feil sertifikater

Bortsett fra feilene ovenfor, kan et håndtrykk mislykkes på grunn av en rekke årsaker knyttet til hvordan vi har opprettet sertifikatene. En vanlig feil er relatert til feil CN. La oss utforske detaljene i servernøkkelbutikken vi opprettet tidligere:

keytool -v -list -keystore serverkeystore.jks

Når vi kjører kommandoen ovenfor, kan vi se detaljene i nøkkelbutikken, spesielt eieren:

... Eier: CN = localhost, OU = teknologi, O = baeldung, L = by, ST = stat, C = xx ...

CN til eieren av dette sertifikatet er satt til localhost. Eierens CN må nøyaktig matche verten til serveren. Hvis det er misforhold, vil det resultere i en SSLHandshakeException.

La oss prøve å regenerere serversertifikatet med CN som noe annet enn localhost. Når vi bruker det regenererte sertifikatet nå til å kjøre SimpleServer og SimpleClient det mislykkes raskt:

Unntak i tråden "hoved" javax.net.ssl.SSLHandshakeException: java.security.cert.CertificateException: Ingen navn som samsvarer med lokal vert funnet

Unntaket ovenfor viser tydelig at klienten forventet et sertifikat med navnet som lokal vert som det ikke fant.

Vær oppmerksom på at JSSE pålegger ikke vertsnavnbekreftelse som standard. Vi har aktivert vertsnavnbekreftelse i SimpleClient gjennom eksplisitt bruk av HTTPS:

SSLParameters sslParams = nye SSLParameters (); sslParams.setEndpointIdentificationAlgorithm ("HTTPS"); ((SSLSocket) tilkobling) .setSSLParameters (sslParams);

Vertsnavnbekreftelse er en vanlig årsak til feil og generelt, og bør alltid håndheves for bedre sikkerhet. For detaljer om vertsnavnbekreftelse og dens betydning for sikkerhet med TLS, se denne artikkelen.

5.5. Inkompatibel SSL-versjon

Foreløpig er det forskjellige kryptografiske protokoller, inkludert forskjellige versjoner av SSL og TLS i drift.

Som nevnt tidligere, er SSL generelt erstattet av TLS for sin kryptografiske styrke. Den kryptografiske protokollen og versjonen er et tilleggselement som en klient og en server må bli enige om under et håndtrykk.

For eksempel, hvis serveren bruker en kryptografisk protokoll av SSL3 og klienten bruker TLS1.3, kan de ikke bli enige om en kryptografisk protokoll og en SSLHandshakeException vil bli generert.

I vår SimpleClient la oss endre protokollen til noe som ikke er kompatibelt med protokollsettet for serveren:

((SSLSocket) tilkobling) .setEnabledProtocols (ny streng [] {"TLSv1.1"});

Når vi kjører vår klient igjen, får vi en SSLHandshakeException:

Unntak i tråden "hoved" javax.net.ssl.SSLHandshakeException: Ingen passende protokoll (protokollen er deaktivert eller krypteringsserier er upassende)

Unntakets spor i slike tilfeller er abstrakte og forteller oss ikke det eksakte problemet. For å løse denne typen problemer er det nødvendig å bekrefte at både klienten og serveren bruker enten de samme eller kompatible kryptografiske protokollene.

5.6. Inkompatibel Cipher-suite

Klienten og serveren må også bli enige om krypteringspakken de vil bruke til å kryptere meldinger.

Under et håndtrykk vil klienten presentere en liste over mulige krypter som skal brukes, og serveren vil svare med en valgt kryptering fra listen. Serveren vil generere en SSLHandshakeException hvis den ikke kan velge en passende kryptering.

I vår SimpleClient la oss endre krypteringspakken til noe som ikke er kompatibelt med krypteringspakken som brukes av serveren vår:

((SSLSocket) tilkobling) .setEnabledCipherSuites (ny streng [] {"TLS_RSA_WITH_AES_128_GCM_SHA256"});

Når vi starter klienten på nytt, får vi en SSLHandshakeException:

Unntak i tråden "hoved" javax.net.ssl.SSLHandshakeException: Mottatt dødelig varsel: handshake_failure

Igjen er unntakssporene ganske abstrakte og forteller oss ikke det eksakte problemet. Løsningen på en slik feil er å verifisere de aktiverte krypteringssuitene som brukes av både klienten og serveren, og sikre at det er minst en felles suite tilgjengelig.

Normalt er klienter og servere konfigurert til å bruke et bredt utvalg av krypteringsserier, slik at denne feilen er mindre sannsynlig. Hvis vi støter på denne feilen, er det vanligvis fordi serveren er konfigurert til å bruke en veldig selektiv kryptering. En server kan velge å håndheve et selektivt sett med krypter av sikkerhetsmessige årsaker.

6. Konklusjon

I denne opplæringen lærte vi om å konfigurere SSL ved hjelp av Java-stikkontakter. Deretter diskuterte vi SSL-håndtrykk med enveis og toveis SSL. Til slutt gikk vi gjennom en liste over mulige årsaker til at SSL-håndtrykk kan mislykkes, og diskuterte løsningene.

Som alltid er koden for eksemplene tilgjengelig på GitHub.

Java bunn

Jeg kunngjorde nettopp det nye Lær våren kurs, med fokus på det grunnleggende i vår 5 og vårstøvel 2:

>> KONTROLLER KURSET

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