'HttpClient'에 해당되는 글 1건
Commons HttpClient로 SSL 통신하기 :: 2009/06/18 23:37
Apache Commons HttpClient는 JDK 1.4부터 등장한 Java Secure Socket Extension (JSSE)를 기반으로 SSL (또는 TLS) 상의 HTTP (HTTP/S) 통신에 대한 지원을 제공한다. Commons HttpClient를 이용한 HTTP/S 통신 방법을 살펴보자.
1. Commons HttpClient 사용하기
일반적으로 Commons HttpClient의 HTTP 통신은 아래와 같다.
정상적인 경우 결과는 아래와 같다.
실패한 경우 결과는 아래와 같다.
2. SSL 통신과 Trusted CA 인증서 등록하기
JSSE가 올바르게 설치되었다면, 기본적으로 HTTP/S 통신도 일반 HTTP 통신과 같이 위와 같은 코드를 그대로 사용할 수 있다. 단, 이 경우에 서버 싸이트 인증서가 클라이언트쪽에 신뢰하는 인증서로서 인식될 수 있어야 한다. 그렇지 않으면 아래와 같은 SSL handshake 오류가 발생한다.
JDK에 의해 제공되어지는 Java Standard Trust Keystore는 ${JAVA_HOME}/jre/lib/security/cacerts에 위치한다. 이 cacerts 키스토어 파일에 대상 서버의 SSL 싸이트 인증서를 발행한 기관의 CA 인증서가 신뢰하는 인증서로 등록되어 있어야 한다. 다음과 같이 keytool.exe를 사용하여 키스토어에 등록된 신뢰하는 인증서 목록을 조회할 수 있다.
다음과 같은 방법으로 키스토어 파일에 Trusted CA 인증서를 추가로 등록할 수 있다. CA 인증서는 웹브라우저에서 열쇠모양의 아이콘을 누르면 해당 싸이트 인증서를 볼 수 있고, 거기에서 인증서를 복사할 수 있다. 아래 예시는 Trusted CA 인증서를 ${JAVA_HOME}\jre\lib\secutiry\cacerts에 등록을 하는 방법이다.
이렇게 서버 CA 인증서가 신뢰하는 인증서로 등록이 되면, 일반 HTTP 통신과 같이 URL이 https://인 주소로 SSL 통신을 정상적으로 할 수 있다. 키스토어 파일에서 인증서를 제거하는 방법은 아래와 같다.
아래처럼 서버 싸이트 인증서를 바로 등록할 수도 있다. 그러나 싸이트 인증서는 보통 Trusted CA 인증서보다 유효기간이 짧아 매번 갱신을 해줘야 할 것이다.
3. Commons HttpClient의 SSL 커스터마이징
기본적인 사용법 이외에 자기서명(self-signed)되었거나 또는 신뢰되지 않은(untrusted) SSL 인증서를 사용하는 경우처럼 SSL 통신을 커스터마이징할 필요가 있을 수 있다.
기본 커스터마이징 방법은 org.apache.commons.httpclient.protocol.SecureProtocolSocketFactory을 구현해서 org.apache.commons.httpclient.protocol.Protocol를 생성하여 등록해주면 된다. 다음과 같은 코드 한 줄을 추가해 주면 된다. 자세한 내용은 이곳을 참조한다.
Commons HttpClient의 contribution 패키지에서 사용할 수 있는 EasySSLProtocolSocketFactory를 사용하면 신뢰되지 않은 자기서명(self-signed)된 인증서를 가진 서버와도 바로 SSL 통신을 할 수 있다. 즉, cacerts 키스토어에 서버 인증서를 별도로 등록할 필요가 없다. 다음과 같이 사용할 수 있다.
호출 로그
참조: http://hc.apache.org/httpclient-3.x/sslguide.html
1. Commons HttpClient 사용하기
일반적으로 Commons HttpClient의 HTTP 통신은 아래와 같다.
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpStatus;
import org.apache.commons.httpclient.methods.GetMethod;
// import org.apache.commons.httpclient.methods.PostMethod;
public class HttpClientSample {
public static void main(String[] args) {
HttpClient httpclient = new HttpClient();
GetMethod httpget = new GetMethod("http://www.java2go.net/");
// PostMethod httppost = new
// PostMethod("https://www.java2go.net/nopage.html");
try {
int statusCode = httpclient.executeMethod(httpget);
System.out.println("Response Status Code: " + statusCode);
System.out.println("Response Status Line: " + httpget.getStatusLine());
System.out.println("Response Body: \n"
+ httpget.getResponseBodyAsString());
if (statusCode == HttpStatus.SC_OK) {
// if (statusCode >= 200 && statusCode < 300) {
System.out.println("Success!");
} else {
System.out.println("Fail!");
}
} catch (Exception e) {
e.printStackTrace();
} finally {
httpget.releaseConnection();
}
}
} |
정상적인 경우 결과는 아래와 같다.
Response Status Code: 200 Response Status Line: HTTP/1.1 200 OK Response Body: <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <title>Java2go.net</title> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> ... 생략 ... </body> </html> Success! |
실패한 경우 결과는 아래와 같다.
Response Status Code: 404 Response Status Line: HTTP/1.1 404 Not Found Response Body: <!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN"> <HTML><HEAD> <TITLE>404 Not Found</TITLE> </HEAD><BODY> <H1>Not Found</H1> The requested URL /nopage.html was not found on this server.<P> <HR> <ADDRESS>Apache/1.3.37p3 Server at java2go.net Port 80</ADDRESS> </BODY></HTML> Fail! |
2. SSL 통신과 Trusted CA 인증서 등록하기
JSSE가 올바르게 설치되었다면, 기본적으로 HTTP/S 통신도 일반 HTTP 통신과 같이 위와 같은 코드를 그대로 사용할 수 있다. 단, 이 경우에 서버 싸이트 인증서가 클라이언트쪽에 신뢰하는 인증서로서 인식될 수 있어야 한다. 그렇지 않으면 아래와 같은 SSL handshake 오류가 발생한다.
javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: No trusted certificate found at com.sun.net.ssl.internal.ssl.BaseSSLSocketImpl.a(DashoA12275) at com.sun.net.ssl.internal.ssl.SSLSocketImpl.a(DashoA12275) at com.sun.net.ssl.internal.ssl.SSLSocketImpl.a(DashoA12275) at com.sun.net.ssl.internal.ssl.SunJSSE_az.a(DashoA12275) at com.sun.net.ssl.internal.ssl.SunJSSE_az.a(DashoA12275) at com.sun.net.ssl.internal.ssl.SunJSSE_ax.a(DashoA12275) at com.sun.net.ssl.internal.ssl.SSLSocketImpl.a(DashoA12275) at com.sun.net.ssl.internal.ssl.SSLSocketImpl.j(DashoA12275) at com.sun.net.ssl.internal.ssl.SSLSocketImpl.a(DashoA12275) at com.sun.net.ssl.internal.ssl.AppOutputStream.write(DashoA12275) ... 생략 ... Caused by: sun.security.validator.ValidatorException: No trusted certificate found at sun.security.validator.SimpleValidator.buildTrustedChain(SimpleValidator.java:304) at sun.security.validator.SimpleValidator.engineValidate(SimpleValidator.java:107) at sun.security.validator.Validator.validate(Validator.java:202) at com.sun.net.ssl.internal.ssl.X509TrustManagerImpl.checkServerTrusted(DashoA12275) at com.sun.net.ssl.internal.ssl.JsseX509TrustManager.checkServerTrusted(DashoA12275) ... 17 more |
JDK에 의해 제공되어지는 Java Standard Trust Keystore는 ${JAVA_HOME}/jre/lib/security/cacerts에 위치한다. 이 cacerts 키스토어 파일에 대상 서버의 SSL 싸이트 인증서를 발행한 기관의 CA 인증서가 신뢰하는 인증서로 등록되어 있어야 한다. 다음과 같이 keytool.exe를 사용하여 키스토어에 등록된 신뢰하는 인증서 목록을 조회할 수 있다.
C:\jdk1.4.2\jre\lib\security>keytool -list -v -keystore cacerts Enter keystore password: changeit Keystore type: jks Keystore provider: SUN Your keystore contains 52 entries Alias name: verisignclass3g2ca Creation date: Jun 16, 2004 Entry type: trustedCertEntry Owner: OU=VeriSign Trust Network, OU="(c) 1998 VeriSign, Inc. - For authorized use only", OU=Class 3 Public Primary Certification Authority - G2, O="VeriSign, Inc.", C=US Issuer: OU=VeriSign Trust Network, OU="(c) 1998 VeriSign, Inc. - For authorized use only", OU=Class 3 Public Primary Certification Authority - G2, O="VeriSign, Inc.", C=US Serial number: 7dd9fe07cfa81eb7107967fba78934c6 Valid from: Mon May 18 09:00:00 KST 1998 until: Wed Aug 02 08:59:59 KST 2028 Certificate fingerprints: MD5: A2:33:9B:4C:74:78:73:D4:6C:E7:C1:F3:8D:CB:5C:E9 SHA1: 85:37:1C:A6:E5:50:14:3D:CE:28:03:47:1B:DE:3A:09:E8:F8:77:0F ******************************************* ******************************************* Alias name: entrustclientca Creation date: Jan 10, 2003 Entry type: trustedCertEntry Owner: CN=Entrust.net Client Certification Authority, OU=(c) 1999 Entrust.net Limited, OU=www.entrust.net/Client_CA_Info/CPS incorp. by ref. limits liab., O=Entrust.net, C=US Issuer: CN=Entrust.net Client Certification Authority, OU=(c) 1999 Entrust.net Limited, OU=www.entrust.net/Client_CA_Info/CPS incorp. by ref. limits liab., O=Entrust.net, C=US Serial number: 380391ee Valid from: Wed Oct 13 04:24:30 KST 1999 until: Sun Oct 13 04:54:30 KST 2019 Certificate fingerprints: MD5: 0C:41:2F:13:5B:A0:54:F5:96:66:2D:7E:CD:0E:03:F4 SHA1: DA:79:C1:71:11:50:C2:34:39:AA:2B:0B:0C:62:FD:55:B2:F9:F5:80 ... 생략 ... |
다음과 같은 방법으로 키스토어 파일에 Trusted CA 인증서를 추가로 등록할 수 있다. CA 인증서는 웹브라우저에서 열쇠모양의 아이콘을 누르면 해당 싸이트 인증서를 볼 수 있고, 거기에서 인증서를 복사할 수 있다. 아래 예시는 Trusted CA 인증서를 ${JAVA_HOME}\jre\lib\secutiry\cacerts에 등록을 하는 방법이다.
C:\j2sdk1.4.2\jre\lib\security>keytool -import -keystore cacerts -file
c:\certs\TradeSignCA.cer -alias tradesignca
Enter keystore password: changeit
Owner: CN=TradeSignCA, OU=AccreditedCA, O=TradeSign, C=KR
Issuer: CN=KISA RootCA 1, OU=Korea Certification Authority Central, O=KISA, C=KR
Serial number: 2764
Valid from: Tue Nov 15 11:14:59 KST 2005 until: Sun Nov 15 11:14:59 KST 2015
Certificate fingerprints:
MD5: C2:E0:27:3D:36:4B:86:29:74:4D:6B:9F:5A:B5:01:26
SHA1: A0:CD:6A:6D:A4:7B:73:15:F5:8A:CB:1F:C6:FD:C2:14:C9:3B:5D:BE
Trust this certificate? [no]: y
Certificate was added to keystore |
이렇게 서버 CA 인증서가 신뢰하는 인증서로 등록이 되면, 일반 HTTP 통신과 같이 URL이 https://인 주소로 SSL 통신을 정상적으로 할 수 있다. 키스토어 파일에서 인증서를 제거하는 방법은 아래와 같다.
C:\jdk1.4.2\jre\lib\security>keytool -delete -keystore cacerts -alias tradesignca Enter keystore password: changeit |
아래처럼 서버 싸이트 인증서를 바로 등록할 수도 있다. 그러나 싸이트 인증서는 보통 Trusted CA 인증서보다 유효기간이 짧아 매번 갱신을 해줘야 할 것이다.
C:\jdk1.4.2\jre\lib\security>keytool -import -keystore cacerts -file
c:\certs\www.java2go.net.cer -alias mykey
Enter keystore password: changeit
Owner: CN=www.java2go.net, OU=KTNET, OU=AccreditedCA, O=TradeSign, C=KR
Issuer: CN=TradeSignCA, OU=AccreditedCA, O=TradeSign, C=KR
Serial number: 596e9cf0
Valid from: Tue May 12 13:37:20 KST 2009 until: Wed May 12 14:07:20 KST 2010
Certificate fingerprints:
MD5: EF:EB:11:66:BD:CC:B1:D4:88:35:AB:25:9F:2F:79:8B
SHA1: DC:C4:31:20:46:25:72:68:8B:96:AC:92:EE:F3:8D:15:EF:A7:46:2D
Trust this certificate? [no]: y
Certificate was added to keystore |
3. Commons HttpClient의 SSL 커스터마이징
기본적인 사용법 이외에 자기서명(self-signed)되었거나 또는 신뢰되지 않은(untrusted) SSL 인증서를 사용하는 경우처럼 SSL 통신을 커스터마이징할 필요가 있을 수 있다.
기본 커스터마이징 방법은 org.apache.commons.httpclient.protocol.SecureProtocolSocketFactory을 구현해서 org.apache.commons.httpclient.protocol.Protocol를 생성하여 등록해주면 된다. 다음과 같은 코드 한 줄을 추가해 주면 된다. 자세한 내용은 이곳을 참조한다.
Protocol.registerProtocol("https",
new Protocol("https", new MySSLSocketFactory(), 443)); |
Commons HttpClient의 contribution 패키지에서 사용할 수 있는 EasySSLProtocolSocketFactory를 사용하면 신뢰되지 않은 자기서명(self-signed)된 인증서를 가진 서버와도 바로 SSL 통신을 할 수 있다. 즉, cacerts 키스토어에 서버 인증서를 별도로 등록할 필요가 없다. 다음과 같이 사용할 수 있다.
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpStatus;
import org.apache.commons.httpclient.contrib.ssl.EasySSLProtocolSocketFactory;
import org.apache.commons.httpclient.methods.GetMethod;
import org.apache.commons.httpclient.protocol.Protocol;
public class HttpClientSample2 {
public static void main(String[] args) {
HttpClient httpclient = new HttpClient();
GetMethod httpget = new GetMethod("https://www.java2go.net/");
try {
Protocol.registerProtocol("https", new Protocol("https",
new EasySSLProtocolSocketFactory(), 443));
int statusCode = httpclient.executeMethod(httpget);
System.out.println("Response Status Code: " + statusCode);
System.out.println("Response Status Line: " + httpget.getStatusLine());
System.out.println("Response Body: \n"
+ httpget.getResponseBodyAsString());
if (statusCode == HttpStatus.SC_OK) {
System.out.println("Success!");
} else {
System.out.println("Fail!");
}
} catch (Exception e) {
e.printStackTrace();
} finally {
httpget.releaseConnection();
}
}
} |
호출 로그
{DEBUG} [2009-06-18 23:29:31,062] <org.apache.commons.httpclient.HttpConnection> ()
: Open connection to www.java2go.net:443 |
참조: http://hc.apache.org/httpclient-3.x/sslguide.html

