HTTPS v Jave
K práci s protokolom HTTPS (Hypertext Transfer Protocol Secure) možno
podobne ako v prípade HTTP použiť triedy z balíka java.net
. Ak
potrebujeme napríklad z nejakého servera stiahnuť webovú stránku cez HTTPS,
stačí vytvoriť objekt java.net.URL
s požadovanou adresou,
otvoriť spojenie az príslušného objektu InputStream
prečítať
dáta.
String address = "https://www.google.com"; URL url = new URL(address); URLConnection connection = url.openConnection(); InputStream in = null; ByteArrayOutputStream out = new ByteArrayOutputStream(); try { in = connection.getInputStream(); byte[] buffer = new byte[8192]; int size; while ((size = in.read(buffer)) != -1) { out.write(buffer, 0, size); } } finally { if (in != null) { in.close(); } } String content = out.toString("UTF-8");
Kód vyššie funguje za predpokladu, že je HTTPS server resp. jeho
certifikát považovaný za dôveryhodný. Ak nie je tomu tak, spojenie zlyhá a
dôjde k vyhodenie výnimky
javax.net.ssl.SSLHandshakeException
.
javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
V tomto článku si ukážeme, ako funguje overovania serverových SSL / TLS certifikátov a kde sú uložené certifikáty dôveryhodných autorít.
Certifikačná cesta
Aby bol certifikát servera považovaný za dôveryhodný, musí byť vydaný dôveryhodnou certifikačnou autoritou. Certifikát certifikačnej autority vydáva buď certifikačná autorita sama, v tomto prípade sa jedná o koreňovú certifikačnú autoritu, alebo jej nadradená certifikačná autorita.
Certifikáty tvoria tzv. Reťazec (chain) resp. certifikačný cestu (certification path), kde na jednom konci stojí certifikát koreňovej certifikačnej autority, na druhom konci certifikát servera a medzi nimi sa nachádza ľubovoľný počet certifikátov podriadených certifikačných autorít.
Truststore
Certifikáty dôveryhodných certifikačných autorít sú v Jave uložené v úložisku dôveryhodných certifikátov, v tzv. Truststore.
Východiskovým úložiskom dôveryhodných certifikátov je súbor
cacerts
v adresári
C:\Program Files\Java\jre7\lib\security
. Umiestnenie adresára sa
môže líšiť podľa verzie JRE. Certifikáty sú uložené vo formáte JKS
(Java KeyStore) a chránené heslom. Predvolené heslo znie
changeit
.
Úložisko je možné spravovať pomocou nástroja keytool
,
ktorý je súčasťou Java SE Development Kit
. Napríklad obsah
možno vypísať príkazom:
keytool -list -storepass changeit -keystore "C:\Program Files\Java\jre7\lib\security\cacerts"
Popis všetkých parametrov nástroje keytool
je k dispozícii
na stránkach
Oracle
Umiestnenie úložiska dôveryhodných certifikátov možno pre daný proces
zmeniť pomocou argumentu JVM javax.net.ssl.trustStore
. Argumentom
javax.net.ssl.trustStorePassword
možno špecifikovať heslo.
Príklad: Spustenie aplikácie s vlastným úložiskom dôveryhodných certifikátov:
java -Djavax.net.ssl.trustStore=mujtruststore.jks -Djavax.net.ssl.trustStorePassword=supertajneheslo -jar moje-aplikace.jar
Trust manager
Niekedy je potrebné mať nad overovaním SSL / TLS certifikátov väčšiu kontrolu. Predstavme si napríklad, že naše aplikácie komunikuje s aplikáciami našich obchodných partnerov. Každý partner má vlastný server s vlastným SSL certifikátom.
Naše aplikácie potrebuje overiť, že certifikát servera prislúcha danému partnerovi. Ďalším príkladom je situácia, keď nechceme vôbec používať file system, ale certifikáty spravujeme v rámci našej aplikácie, uložené napr. V databáze. Alebo si predstavme prípad, kedy certifikáty nechceme overovať vôbec.
Pre všetky tieto prípady môžeme použiť vlastnú implementáciu
rozhrania javax.net.ssl.X509TrustManager
. Overenie certifikátov
servera sa deje v metódou checkServerTrusted
. Metóda vyhadzuje
výnimku CertificateException
, ak overenie zlyhá a certifikát nie
je považovaný za dôveryhodný.
Príklad: Náš vlastný trust manager, ktorý akceptuje ľubovoľný certifikát servera.
public class MyTrustManager implements X509TrustManager { @Override public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException { throw new CertificateException(); } @Override public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException { /* Ověřit řetězec certifikátů */ } @Override public X509Certificate[] getAcceptedIssuers() { return new X509Certificate[0]; } }
Nasledujúci kód pre dané HTTPS spojenie nastaví náš trust manager.
Trust manager je ukrytý v objekte triedy SSLSocketFactory
, ktorý
najprv vytvoríme pomocou triedy SSLContext
. Objekt
SSLSocketFactory
nastavíme pre dané pripojenie metódou
setSSLSocketFactory
rozhranie HttpsURLConnection
.
MyTrustManager trustManager = new MyTrustManager(); TrustManager[] tm = { trustManager }; SSLContext context = SSLContext.getInstance("TLS"); context.init(null, tm, null); SSLSocketFactory socketFactory = context.getSocketFactory(); String address = "https://localhost"; URL url = new URL(address); URLConnection connection = url.openConnection(); if (connection instanceof HttpsURLConnection) { ((HttpsURLConnection) connection).setSSLSocketFactory(socketFactory); }
Alternatívne je možné pomocou metódy SSLContext.setDefault
nastaviť SSL kontext s vlastným trust managerom globálne pre celú
aplikáciu.
Zhrnutie
Čo teda robiť, ak v našej aplikácii dochádza k
SSLHandshakeException
pri pokuse o pripojenie na server cez
HTTPS?
Možnosti máme tieto:
- Pridať certifikát servera resp. certifikát certifikačnej autority do
východzieho úložiska dôveryhodných certifikátov do súboru
cacerts
. - Uložiť certifikát do vlastného úložiska a spustiť aplikáciu s
argumentmi JVM
javax.net.ssl.trustStore
ajavax.net.ssl.trustStorePassword
. - Použiť vlastnú implementáciu
X509TrustManager
.