Izgradite sigurne mrežne aplikacije s SSL-om i JSSE API-jem

Internet je opasno mjesto. Jednostavno je previše lako prikrasti, lažirati i ukrasti nezaštićene podatke dok putuju preko žica. Prošli mjesec napisao sam posljednji članak u nizu o certifikatima X.509 i infrastrukturi javnih ključeva (PKI), tehnologijama koje osiguravaju većinu aktivnosti e-trgovine na Internetu. Pri kraju članka predložio sam da pogledam protokol SSL (Secure Socket Layer) kako bih saznao kako se X.509 certifikati koriste u praksi. SSL je ubojita aplikacija X.509 - podržavaju je gotovo svi preglednici i najpopularniji web i poslužitelji aplikacija.

Ovog mjeseca istražit ću SSL kako ga implementira JSSE (Java Secure Socket Extension) i pokazati vam kako graditi sigurne mrežne programe na Javi koristeći SSL i JSSE.

Počnimo s jednostavnom demonstracijom. JSSE nudi SSL alat za Java programe. Pored potrebnih klasa i sučelja, JSSE nudi i praktičnu sklopku za otklanjanje pogrešaka naredbenog retka pomoću koje možete gledati SSL protokol u akciji. Uz pružanje korisnih informacija za otklanjanje pogrešaka u nepokornoj aplikaciji, igranje s alatom izvrstan je način da namočite noge SSL-om i JSSE-om.

Da biste pokrenuli demonstraciju, prvo morate kompajlirati sljedeću klasu:

test javne klase {public static void main (String [] arstring) {try {new java.net.URL ("//" + arstring [0] + "/"). getContent (); } catch (iznimka iznimke) {iznimka.printStackTrace (); }}}

Dalje, morate uključiti SSL otklanjanje pogrešaka i pokrenuti gornju aplikaciju. Aplikacija se povezuje sa sigurnom web stranicom koju navedete u naredbenom retku pomoću SSL protokola putem HTTPS-a. Prva opcija učitava rukovatelj protokolom HTTPS. Druga opcija, opcija otklanjanja pogrešaka, uzrokuje da program ispiše svoje ponašanje. Evo naredbe (zamijenite imenom sigurnog web poslužitelja):

 java -Djava.protocol.handler.pkgs = com.sun.net.ssl.internal.www.protocol -Djavax.net.debug = ssl Test  

Trebate instalirati JSSE; pogledajte resurse ako niste sigurni kako.

Ajmo sad prijeći na posao i razgovarati o SSL-u i JSSE-u.

Kratki pogled na SSL

Kôd u uvodu pokazuje najlakši način dodavanja SSL-a u svoje programe - putem java.net.URLklase. Ovaj je pristup koristan, ali nije dovoljno fleksibilan da vam omogućuje stvaranje sigurne aplikacije koja koristi generičke utičnice.

Prije nego što vam pokažem kako dodati tu fleksibilnost, pogledajmo na brzinu SSL-ove značajke.

Kao što mu samo ime govori, SSL ima za cilj pružiti aplikacijama siguran set alata poput soketa. U idealnom slučaju, trebalo bi biti lako pretvoriti aplikaciju koja koristi redovite utičnice u aplikaciju koja koristi SSL.

SSL rješava tri važna sigurnosna problema:

  1. Pruža provjeru autentičnosti, što pomaže osigurati legitimitet entiteta koji su uključeni u dijalog.
  2. Pruža privatnost. SSL jamči da treća strana ne može dešifrirati dijalog između dva entiteta.
  3. Održava integritet. Korištenje MAC-a (kod za provjeru autentičnosti poruke), koji je sličan kontrolnom zbroju, pomaže zajamčiti da treća strana ne mijenja dijalog između dva entiteta.

SSL se u velikoj mjeri oslanja na kriptografiju s javnim i tajnim ključevima. Koristi kriptografiju tajnog ključa za skupno šifriranje podataka razmijenjenih između dvije aplikacije. SSL pruža idealno rješenje jer su algoritmi s tajnim ključem istovremeno sigurni i brzi. Kriptografija s javnim ključem, koja je sporija od kriptografije s tajnim ključem, bolji je izbor za provjeru autentičnosti i razmjenu ključeva.

Sunčeva JSSE referentna implementacija dolazi sa svom tehnologijom potrebnom za dodavanje SSL-a u vaše aplikacije. Uključuje podršku za kriptografiju RSA (Rivest-Shamir-Adleman) - de facto standard za sigurnost na Internetu. Uključuje implementaciju SSL 3.0 - trenutni SSL standard - i TLS (Transport Layer Security) 1.0, sljedeću generaciju SSL-a. JSSE također nudi paket API-ja za stvaranje i upotrebu sigurnih utičnica.

JSSE API

Java sigurnosna arhitektura intenzivno koristi obrazac tvorničkog dizajna. Za neupućene, obrazac tvorničkog dizajna koristi posebne tvorničke objekte za konstruiranje instanci, umjesto da izravno poziva svoje konstruktore. (Pogledajte Resursi za prednosti i nedostatke tvorničke klase.)

U JSSE-u sve započinje s tvornicom; postoji tvornica za SSL utičnice i tvornica za SSL utičnice poslužitelja. Budući da su generičke utičnice i poslužiteljske utičnice već prilično ključne za Java mrežno programiranje, pretpostavit ću da ste ih upoznali i da razumijete njihove uloge i razlike. Ako niste, preporučujem da uzmete dobru knjigu o Java mrežnom programiranju.

SSLSocketFactory

Metode u javax.net.ssl.SSLSocketFactoryrazredu spadaju u tri kategorije. Prvi se sastoji od jedne statičke metode koje dohvaća zadani SSL Socket tvornice: static SocketFactory getDefault().

Druga kategorija sastoji se od četiri metode naslijeđene iz javax.net.SocketFactorytog zrcala četiri konstruktora ključa pronađena u java.net.Socketklasi i jedne metode koja omotava postojeću utičnicu SSL utičnicom. Svaki od njih vraća SSL utičnicu:

  1. Socket createSocket(String host, int port)
  2. Socket createSocket(String host, int port, InetAddress clientHost, int clientPort)
  3. Socket createSocket(InetAddress host, int port)
  4. Socket createSocket(InetAddress host, int port, InetAddress clientHost, int clientPort)
  5. Socket createSocket(Socket socket, String host, int port, boolean autoClose)

Dvije metode u trećoj kategoriji vraćaju popis paketa SSL šifri koji su omogućeni prema zadanim postavkama i potpuni popis podržanih paketa SSL šifri:

  1. String [] getDefaultCipherSuites()
  2. String [] getSupportedCipherSuites()

Skup šifri kombinacija je kriptografskih algoritama koji definiraju određenu razinu sigurnosti za SSL vezu. Skup šifri definira je li veza šifrirana, je li provjeren integritet sadržaja i kako dolazi do provjere autentičnosti.

SSLServerSocketFactory

Metode na javax.net.ssl.SSLServerSocketFactorysatu spadaju u iste tri kategorije kao i SSLSocketFactory. Prvo, tu je jedna statička metoda koja dohvaća zadani SSL poslužitelja socket tvornice: static ServerSocketFactory getDefault().

Metode koje vraćaju utičnice SSL poslužitelja zrcale konstruktore pronađene u java.net.ServerSocketklasi:

  1. ServerSocket createServerSocket(int port)
  2. ServerSocket createServerSocket(int port, int backlog)
  3. ServerSocket createServerSocket(int port, int backlog, InetAddress address)

Konačno, SSLServerSocketFactoryznačajke su dvije metode koje vraćaju popis šifri omogućenih prema zadanim postavkama i popis podržanih šifri:

  1. String [] getDefaultCipherSuites()
  2. String [] getSupportedCipherSuites()

Zasad je API prilično jednostavan.

SSLSocket

Stvari postaju zanimljive u javax.net.ssl.SSLSocketrazredu. Pretpostavljam da ste već upoznati s metodama koje pruža njegov roditelj, Socketklasa, pa ću se usredotočiti na metode koje pružaju funkcionalnost povezanu sa SSL-om.

Kao i dvije tvorničke klase SSL-a, prve dvije metode navedene u nastavku dohvaćaju omogućeni i podržani paket SSL šifri. Treća metoda postavlja omogućene pakete šifri. Aplikacija može koristiti treću operaciju za nadogradnju ili snižavanje opsega prihvatljive sigurnosti koju će aplikacija omogućiti:

  1. String [] getEnabledCipherSuites()
  2. String [] getSupportedCipherSuites()
  3. void setEnabledCipherSuites(String [] suites)

Ove dvije metode određuju može li soket uspostaviti nove SSL sesije, koje održavaju detalje veze - poput zajedničkog tajnog ključa - između veza:

  1. boolean getEnableSessionCreation()
  2. void setEnableSessionCreation(boolean flag)

Sljedeće dvije metode određuju hoće li utičnica zahtijevati provjeru identiteta klijenta. Metode imaju smisla samo kada se pozivaju u utičnicama načina poslužitelja. Zapamtite, prema SSL specifikaciji, provjera autentičnosti klijenta nije obavezna. Na primjer, većina web aplikacija to ne zahtijeva:

  1. boolean getNeedClientAuth()
  2. void setNeedClientAuth(boolean need)

Metode u nastavku mijenjaju utičnicu iz klijentskog u poslužiteljski način. To utječe na to tko pokreće SSL rukovanje i tko prvi provjerava autentičnost:

  1. boolean getUseClientMode()
  2. void setUseClientMode(boolean mode)

Metoda void startHandshake()prisiljava SSL rukovanje. Moguće je, ali nije uobičajeno, prisiliti novu operaciju rukovanja u postojećoj vezi.

Metoda SSLSession getSession()dohvaća SSL sesiju. Rijetko ćete trebati izravno pristupiti SSL sesiji.

Dvije metode navedene u nastavku dodaju i uklanjaju SSL objekt slušatelja rukovanja. Objekt slušatelja rukovanja obavještava se svaki put kada se SSL operacija rukovanja završi na utičnici.

  1. void addHandshakeCompletedListener(HandshakeCompletedListener listener)
  2. void removeHandshakeCompletedListener(HandshakeCompletedListener listener)

SSLServerSocket

javax.net.ssl.SSLServerSocketKlasa je sličan javax.net.ssl.SSLSocketklase; ne zahtjeva puno individualne pažnje. U stvari, skup metoda na javax.net.ssl.SSLServerSocketklasi je podskup metoda na javax.net.ssl.SSLSocketklasi.

Prve dvije metode navedene u nastavku dohvaćaju omogućene i podržane pakete SSL šifri. Treća metoda postavlja omogućeni paket šifri:

  1. String [] getEnabledCipherSuites()
  2. String [] getSupportedCipherSuites()
  3. void setEnabledCipherSuites(String [] suites)

Ove dvije metode kontroliraju može li utičnica poslužitelja uspostaviti nove SSL sesije ili ne:

  1. boolean getEnableSessionCreation()
  2. void setEnableSessionCreation(boolean flag)

Sljedeće metode određuju hoće li prihvaćene utičnice zahtijevati provjeru autentičnosti klijenta:

  1. boolean getNeedClientAuth()
  2. void setNeedClientAuth(boolean flag)

Metode u nastavku mijenjaju prihvaćenu utičnicu iz klijentskog u poslužiteljski način:

  1. boolean getUseClientMode()
  2. void setUseClientMode(boolean flag)

Jednostavan primjer

Da bih ovaj vodič s alatima učinio jasnijim, u nastavku sam uključio izvorni kod za jednostavni poslužitelj i kompatibilni klijent. To je sigurna varijacija tipične aplikacije za odjek koju nude mnogi uvodni mrežni tekstovi.

Poslužitelj, prikazan u nastavku, koristi JSSE za stvaranje sigurne utičnice poslužitelja. Sluša na utičnici poslužitelja za veze sa sigurnih klijenata. Prilikom izvođenja poslužitelja morate navesti pohranu ključeva koju ćete koristiti. Pohrana ključeva sadrži certifikat poslužitelja. Stvorio sam jednostavnu pohranu ključeva koja sadrži jedan certifikat. (Pogledajte Resursi za preuzimanje certifikata.)

import java.io.InputStream; uvoz java.io.InputStreamReader; import java.io.BufferedReader; import java.io.IOException; uvoz javax.net.ssl.SSLSocket; uvoz javax.net.ssl.SSLServerSocket; uvoz javax.net.ssl.SSLServerSocketFactory; javna klasa EchoServer {public static void main (String [] arstring) {try {SSLServerSocketFactory sslserversocketfactory = (SSLServerSocketFactory) SSLServerSocketFactory.getDefault (); SSLServerSocket sslserversocket = (SSLServerSocket) sslserversocketfactory.createServerSocket (9999); SSLSocket sslsocket = (SSLSocket) sslserversocket.accept (); InputStream inputstream = sslsocket.getInputStream (); InputStreamReader inputstreamreader = novi InputStreamReader (inputstream); BufferedReader bufferedreader = novi BufferedReader (inputstreamreader); Niz niza = null; while ((string = puferirani čitač.readLine ())!= null) {System.out.println (niz); System.out.flush (); }} catch (iznimka iznimke) {iznimka.printStackTrace (); }}}

Use the following command to start the server (foobar is both the name of the keystore file and its password):

 java -Djavax.net.ssl.keyStore=foobar -Djavax.net.ssl.keyStorePassword=foobar EchoServer 

The client, shown below, uses JSSE to securely connect to the server. When running the client, you must specify the truststore to use, which contains the list of trusted certificates. I have created a simple truststore that contains a single certificate. (See Resources to download the certificate.)

import java.io.InputStream; import java.io.OutputStream; import java.io.InputStreamReader; import java.io.OutputStreamWriter; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.IOException; import javax.net.ssl.SSLSocket; import javax.net.ssl.SSLSocketFactory; public class EchoClient { public static void main(String [] arstring) { try { SSLSocketFactory sslsocketfactory = (SSLSocketFactory)SSLSocketFactory.getDefault(); SSLSocket sslsocket = (SSLSocket)sslsocketfactory.createSocket("localhost", 9999); InputStream inputstream = System.in; InputStreamReader inputstreamreader = new InputStreamReader(inputstream); BufferedReader bufferedreader = new BufferedReader(inputstreamreader); OutputStream outputstream = sslsocket.getOutputStream(); OutputStreamWriter outputstreamwriter = new OutputStreamWriter(outputstream); BufferedWriter bufferedwriter = new BufferedWriter(outputstreamwriter); String string = null; while ((string = bufferedreader.readLine()) != null) { bufferedwriter.write(string + '\n'); bufferedwriter.flush(); } } catch (Exception exception) { exception.printStackTrace(); } } } 

Upotrijebite sljedeću naredbu za pokretanje klijenta ( foobari naziv je datoteke povjerenja i njegova lozinka):

 java -Djavax.net.ssl.trustStore = foobar -Djavax.net.ssl.trustStorePassword = foobar EchoClient