- HttpsClientWithCustomCert.java - 사용자가 만든 인증서를 이용한 서버 인증
- HttpsClientWithDefaultCACert.java - 공인 인증된 인증서로 서버 인증
- HttpsClientWithoutValidation.java - 인증 없이 서버 접속
- SimpleHttpsServer.java - 사용자가 만든 인증키를 이용하는 간단한 HTTPS 서버
- keystore.jks - 서버(SimpleHttpsServer) 에서 사용하는 인증키 저장소
- truststore.jks - 클라이언트(HttpsClientWithCustomCert) 에서 서버 인증시 사용하는 인증서 저장소
다음과 같은 순서로 설명 1. HttpsURLConnection 을 이용한 접속 (인증 과정 없음) 2. 공인 인증된 인증서로 서버 인증 3. 사용자가 만든 인증서로 서버 인증
1. HttpsURLConnection 을 이용한 접속 (인증 과정 없음)
인증 과정을 결정하는 부분은 SSLContext.init() 함수이다.
아래 소스에는 init() 함수에 모두 null 을 집어 넣음으로써 인증 과정을 무시하고 서버의 내용물을 받는다.
소스:
package com.test.https.practice;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URL;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
/**
*
*/
public class HttpsClientWithoutValidation {
/**
*
* @param urlString
* @throws IOException
* @throws NoSuchAlgorithmException
* @throws KeyManagementException
*/
public void getHttps(String urlString) throws IOException, NoSuchAlgorithmException, KeyManagementException {
// Get HTTPS URL connection
URL url = new URL(urlString);
HttpsURLConnection conn = (HttpsURLConnection)url.openConnection();
// Set Hostname verification
conn.setHostnameVerifier(new HostnameVerifier() {
@Override
public boolean verify(String hostname, SSLSession session) {
// Ignore host name verification. It always returns true.
return true;
}
});
// SSL setting
SSLContext context = SSLContext.getInstance("TLS");
context.init(null, null, null); // No validation for now
conn.setSSLSocketFactory(context.getSocketFactory());
// Connect to host
conn.connect();
conn.setInstanceFollowRedirects(true);
// Print response from host
InputStream in = conn.getInputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(in));
String line = null;
while ((line = reader.readLine()) != null) {
System.out.printf("%s\n", line);
}
reader.close();
}
/**
*
* @param args
* @throws Exception
*/
public static void main(String[] args) throws Exception {
HttpsClientWithoutValidation test = new HttpsClientWithoutValidation();
test.getHttps("https://www.google.com");
}
}
결과:
2. 공인 인증된 인증서로 서버 인증
SSLContext.init() 함수의 두번째 인자에 X509TrustManager 를 선언하여 집어 넣었다.
그리고 JRE 를 설치하면 기본적으로 [JRE 경로]/lib/security/cacerts 라는 파일명의 공인 인증된 인증서 저장소 파일이 있다.
해당 인증서 저장소를 이용하여 서버의 인증서를 검사하게 하였다.
소스:
package com.test.https.practice;
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URL;
import java.security.KeyManagementException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509TrustManager;
/**
*
*/
public class HttpsClientWithDefaultCACert {
/**
*
* @param urlString
* @throws IOException
* @throws NoSuchAlgorithmException
* @throws KeyManagementException
*/
public void getHttps(String urlString) throws IOException, NoSuchAlgorithmException, KeyManagementException {
// Get HTTPS URL connection
URL url = new URL(urlString);
HttpsURLConnection conn = (HttpsURLConnection)url.openConnection();
// Set Hostname verification
conn.setHostnameVerifier(new HostnameVerifier() {
@Override
public boolean verify(String hostname, SSLSession session) {
// Ignore host name verification. It always returns true.
return true;
}
});
// SSL setting
SSLContext context = SSLContext.getInstance("TLS");
context.init(null, new TrustManager[] { new X509TrustManager() {
@Override
public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
// client certification check
}
@Override
public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
// Server certification check
try {
// Get trust store
KeyStore trustStore = KeyStore.getInstance("JKS");
String cacertPath = System.getProperty("java.home") + "/lib/security/cacerts"; // Trust store path should be different by system platform.
trustStore.load(new FileInputStream(cacertPath), "changeit".toCharArray()); // Use default certification validation
// Get Trust Manager
TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmf.init(trustStore);
TrustManager[] tms = tmf.getTrustManagers();
((X509TrustManager)tms[0]).checkServerTrusted(chain, authType);
} catch (KeyStoreException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public X509Certificate[] getAcceptedIssuers() {
return null;
}
} }, null);
conn.setSSLSocketFactory(context.getSocketFactory());
// Connect to host
conn.connect();
conn.setInstanceFollowRedirects(true);
// Print response from host
InputStream in = conn.getInputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(in));
String line = null;
while ((line = reader.readLine()) != null) {
System.out.printf("%s\n", line);
}
reader.close();
}
/**
*
* @param args
* @throws Exception
*/
public static void main(String[] args) throws Exception {
HttpsClientWithDefaultCACert test = new HttpsClientWithDefaultCACert();
test.getHttps("https://www.google.com");
}
}
서버는 지정된 인증키 저장소를 이용해 클라이언트에 자신을 인증하는 키를 보내게 된다. 인증과정이 성공하면 클라이언트에 html 내용물을 전달하고 종료한다.
클라이언트는 지정된 인증서 저장소를 이용해 서버를 인증한다. 인증과정이 성공하면 서버로 부터 html 내용물을 전달받는다.