자바 SSL 사용법
POP3
POP3에 암호화된 연결(SSL : Secure Sockets Layer)이 필요한 경우가 종종 있다. JSSE(Java Secure Socket Extension, J2SE 1.4 이후 버전에는 JSSE가 포함되어 있다.)를 이용하여 SSL 연결을 시도할 때 아래와 같은 exception이 발생할 수 있다. 공인 인증기관에서 받은 인증서가 아닌 개인 인증서를 사용하는 경우 많이 발생할 수 있다.
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
그렇다면 어떻게 해야 하느냐?? 여러 가지 방법이 있지만, 간단한 방법은 서버의 인증서를 신뢰할 수 있는 인증서 목록(KeyStore)에 추가하면 된다. 인증서를 추가하려면 우선 인증서를 받아야겠다. 인증서는 첨부된 파일을 받아서 컴파일하고 실행하면 된다. 실행시키면 아래와 같은 화면이 나타날 것이다. "ecc.fedora.redhat.com" 부분에 인증서를 원하는 서버의 ip와 port를 써주면 된다.
% java InstallCert ecc.fedora.redhat.com
Loading KeyStore C:\Program Files\Java\jre1.5.0_08\lib\security\cacerts...
Opening connection to ecc.fedora.redhat.com:443...
Starting SSL handshake...
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
at com.sun.net.ssl.internal.ssl.Alerts.getSSLException(Alerts.java:150)
at com.sun.net.ssl.internal.ssl.SSLSocketImpl.fatal(SSLSocketImpl.java:1476)
at com.sun.net.ssl.internal.ssl.Handshaker.fatalSE(Handshaker.java:174)
at com.sun.net.ssl.internal.ssl.Handshaker.fatalSE(Handshaker.java:168)
at com.sun.net.ssl.internal.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:846)
at com.sun.net.ssl.internal.ssl.ClientHandshaker.processMessage(ClientHandshaker.java:106)
at com.sun.net.ssl.internal.ssl.Handshaker.processLoop(Handshaker.java:495)
at com.sun.net.ssl.internal.ssl.Handshaker.process_record(Handshaker.java:433)
at com.sun.net.ssl.internal.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:815)
at com.sun.net.ssl.internal.ssl.SSLSocketImpl.performInitialHandshake(SSLSocketImpl.java:1025)
at com.sun.net.ssl.internal.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1038)
at InstallCert.main(InstallCert.java:63)
Caused by: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
at sun.security.validator.PKIXValidator.doBuild(PKIXValidator.java:221)
at sun.security.validator.PKIXValidator.engineValidate(PKIXValidator.java:145)
at sun.security.validator.Validator.validate(Validator.java:203)
at com.sun.net.ssl.internal.ssl.X509TrustManagerImpl.checkServerTrusted(X509TrustManagerImpl.java:172)
at InstallCert$SavingTrustManager.checkServerTrusted(InstallCert.java:158)
at com.sun.net.ssl.internal.ssl.JsseX509TrustManager.checkServerTrusted(SSLContextImpl.java:320)
at com.sun.net.ssl.internal.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:839)
... 7 more
Caused by: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
at sun.security.provider.certpath.SunCertPathBuilder.engineBuild(SunCertPathBuilder.java:236)
at java.security.cert.CertPathBuilder.build(CertPathBuilder.java:194)
at sun.security.validator.PKIXValidator.doBuild(PKIXValidator.java:216)
... 13 more
Server sent 2 certificate(s):
1 Subject CN=ecc.fedora.redhat.com, O=example.com, C=US
Issuer CN=Certificate Shack, O=example.com, C=US
sha1 2e 7f 76 9b 52 91 09 2e 5d 8f 6b 61 39 2d 5e 06 e4 d8 e9 c7
md5 dd d1 a8 03 d7 6c 4b 11 a7 3d 74 28 89 d0 67 54
2 Subject CN=Certificate Shack, O=example.com, C=US
Issuer CN=Certificate Shack, O=example.com, C=US
sha1 fb 58 a7 03 c4 4e 3b 0e e3 2c 40 2f 87 64 13 4d df e1 a1 a6
md5 72 a0 95 43 7e 41 88 18 ae 2f 6d 98 01 2c 89 68
Enter certificate to add to trusted keystore or 'q' to quit: [1]
실행시키면 SSL handshake가 일어난다. 화면에는 exception 부분도 출력된다. exception이 출력되는 이유는 서버에서 사용하는 인증서가 없기 때문에 SSL 연결을 성공할 수 없기 때문이다. 자, 인증서를 받자. 그리고 화면에는 trusted KeyStore에 인증서를 추가할 것인지 묻고 있다. sha1과 md5를 확인해서 올바른 인증서인지 확인한 다음, 올바른 인증서인 경우 "1"을 입력해서 인증서를 받으면 되고, 받고 싶지 않은 경우는 "q"를 입력해서 프로그램을 종료시키면 된다. 인증서를 받게 되면 아래와 같은 화면이 출력되고, "jssecacerts" 파일로 인증서가 저장된다.
[
[
Version: V3
Subject: CN=ecc.fedora.redhat.com, O=example.com, C=US
Signature Algorithm: MD5withRSA, OID = 1.2.840.113549.1.1.4
Key: SunPKCS11-Solaris RSA public key, 1024 bits (id 5158256, session object)
modulus: 1402933022884660852748661816869706021655226675890
635441166580364941191074987345500771612454338502131694873337
233737712894815966313948609351561047977102880577818156814678
041303637255354084762814638611185951230474669455913908815827
173696651397340074281578017567044868711049821409365743953199
69584127568303024757
public exponent: 65537
Validity: [From: Wed Jan 18 13:16:12 PST 2006,
To: Wed Apr 18 14:16:12 PDT 2007]
Issuer: CN=Certificate Shack, O=example.com, C=US
SerialNumber: [ 0f]
Certificate Extensions: 2
[1]: ObjectId: 2.16.840.1.113730.1.1 Criticality=false
NetscapeCertType [
SSL server
]
[2]: ObjectId: 2.5.29.15 Criticality=false
KeyUsage [
Key_Encipherment
]
]
Algorithm: [MD5withRSA]
Signature:
0000: 6D F4 2A 63 76 2A 05 70 A2 21 0E 1E 4A 31 BE 6B m.*cv*.p.!..J1.k
0010: 15 64 D8 BB 35 36 82 B0 0D 2A 96 FA 7A 9F A1 59 .d..56...*..z..Y
0020: CA 90 C3 28 C5 A6 9B 59 05 3B EB B2 8D C9 5E 38 ...(...Y.;....^8
0030: 62 ED 1A D7 93 DF 2A A5 D6 54 94 23 15 A2 0C E5 b.....*..T.#....
0040: 13 40 2C 3E 59 E4 2A EB 51 AC 9E 28 44 23 87 B1 .@,>Y.*.Q..(D#..
0050: 34 0B AC F3 E0 39 CA B8 35 B4 78 07 BF 28 4C C4 4....9..5.x..(L.
0060: 9A 2B A3 E9 04 26 78 19 F0 62 EA 0A B5 BB DC 0B .+...&x..b......
0070: 90 59 E7 77 90 F8 BC 8A 1B 74 4B 4D C1 F8 3B 6C .Y.w.....tKM..;l
]
Added certificate to keystore 'jssecacerts' using alias 'ecc.fedora.redhat.com-1'
이제 이 인증서를 어떻게 사용할까?? 여기에도 몇 가지 방법이 있다. 가장 간단한 방법은 "$JAVA_HOME/jre/lib/security" 폴더에 받은 인증서 파일을 복사하는 방법이다. (이 경우는 파일명을 변경하면 인증서를 찾지 못 한다. 원인은 아직까지는 잘 모르겠다. 본인은 이 방법 대신 다음 방법을 사용한다.)
그리고 다른 방법은 인증서를 원하는 위치로 옮기고 (이 경우는 파일명을 변경해도 상관 없다.) SSL 연결이 필요한 java 명령을 실행할 때, VM 옵션으로 "-Djavax.net.ssl.trustStore=인증서의 경로(인증서 파일명 포함)"를 추가해주면 된다. 다시 첨부 파일을 실행시켰을 때, 아까와는 다른 아래와 같은 화면이 출력되면 성공이다.
% java InstallCert ecc.fedora.redhat.com
Loading KeyStore jssecacerts...
Opening connection to ecc.fedora.redhat.com:443...
Starting SSL handshake...
No errors, certificate is already trusted
Server sent 2 certificate(s):
1 Subject CN=ecc.fedora.redhat.com, O=example.com, C=US
Issuer CN=Certificate Shack, O=example.com, C=US
sha1 2e 7f 76 9b 52 91 09 2e 5d 8f 6b 61 39 2d 5e 06 e4 d8 e9 c7
md5 dd d1 a8 03 d7 6c 4b 11 a7 3d 74 28 89 d0 67 54
2 Subject CN=Certificate Shack, O=example.com, C=US
Issuer CN=Certificate Shack, O=example.com, C=US
sha1 fb 58 a7 03 c4 4e 3b 0e e3 2c 40 2f 87 64 13 4d df e1 a1 a6
md5 72 a0 95 43 7e 41 88 18 ae 2f 6d 98 01 2c 89 68
Enter certificate to add to trusted keystore or 'q' to quit: [1]
q
KeyStore not changed
알고보면 간단하다. 첨부된 파일로 인증서를 받고, POP3 연결할 때 인증서를 사용하도록 VM 옵션을 추가해주기만 하면된다. 첨부파일에 관한 더 자세한 내용은 첨부파일을 열어 소스를 보기 바란다.
SMTP
[JavaMail] 보내는 메일 서버(SMTP) 인증이 필요한 경우
JavaMail API를 사용하여야 한다. 그런데 연구실 서버가 메일을 보낼 때 SMTP로 인증을 받아야 외부 메일로 발송이 가능하다. 내부로 보내는 것은 SMTP인증이 없이도 가능하다. 음... 한참을 해멨다. 분명히 Authenticator 클래스 까지 상속받아서 구현까지 했는데 getPasswordAuthentication() 메소드 조차 호출을 하지 않는 것이다. 해결 방법은 간단했다. Properties에 SMTP 인증을 사용한다고 지정해주기만 하면 된다.
이런 간단한 것 때문에 몇시간을 날린겨 -ㅅ-; 아래는 초간단 예제 코드
String host = "hostAddress";
String user = "xxx";
String password = "yyy";
String from = "xxx@fromHostAddress";
String to = "zzz@toHostAddress";
Properties properties = System.getProperties();
properties.put("mail.smtp.host", host);
properties.put("mail.smtp.auth", "true");
// SMTP 인증이 필요한 경우 반드시 Properties에 SMTP 인증을 사용한다고 설정하여야 한다. 그렇지 않으면 인증을 시도조차 하지 않는다.
// 그리고 Authenticator 클래스를 상속받은 MyAuthenticator 클래스를 생성한다. getPasswordAuthentication() 메소드만 override하면 된다.
// 머 사실 다른 메소드는 final 메소드여서 override할 수 조차 없다. -ㅅ-;
Authenticator authenticator = new MyAuthenticator(user, password);
Session session = Session.getDefaultInstance(properties, authenticator);
MimeMessage message = new MimeMessage(session);
InternetAddress fromAddress = new InternetAddress(from, "sender", "euc-kr");
InternetAddress toAddress = new InternetAddress(to, "receiver", "euc-kr");
message.setFrom(fromAddress);
message.addRecipient(MimeMessage.RecipientType.TO, toAddress);
message.setSubject("mail test");
message.setText("this is test mail");
Transport.send(message);
'Computer Science' 카테고리의 다른 글
How to run HelloWorld and tests of cocos2d-x on bada (0) | 2012.01.05 |
---|---|
VISUALSVN SERVER // Installing Trac with VisualSVN Server (0) | 2012.01.02 |
Apache HttpClient로 https 연결 데이터 받기 (0) | 2011.12.30 |
[알아봅시다] 망중립성 가이드라인 (0) | 2011.12.25 |
[알아봅시다] 안드로이드 4.0 ICS (0) | 2011.12.21 |