이메일 서버에 SSL 로 접속할 때, 인증서 오류를 무시하는 방법
1. 개요
1.1. 에러 메시지
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
1.2. 원인
SSL 방식으로 이메일 서버에 접속을 시도하는데, 서버쪽 인증서를 신뢰할 수 없을 때 발생하는 오류이다.
- Let's Encrypt 와 같이 클라이언트에서 알지 못하는 인증기관에서 인증서를 발급받은 경우(클라이언트 쪽 Java 의 신뢰하는 인증기관 인증서 목록(keystore)에 없는 경우)
- 서버 인증서의 기간이 만료된 경우
- 인증기관을 통하지 않고 스스로 인증서를 발급한 경우
- 서버이름과 인증기관에서 발급한 인증서의 호스트가 다른 경우
- 기타 상상력이 필요한 경우
첫 번째 것은 처리 방법을 찾을 필요가 있다.
1.3. 해결방안
인터넷에 떠도는 2가지 방법이 있다(원저자를 정확히 알기는 힘들다).
- 서버 인증서를 클라이언트로 다운받아 설치하는 방법
- TrustManager를 구현하여 서버쪽 인증서의 신뢰여부를 검사하지 않도록 하는 방법
첫 번째는 InstallCert.java 라는 파일을 이용한 방법이고, 안전한 방법이라고 할 수 있으나, 대체로 적절한 방법이 아닐 가능성이 높다.
두 번째는 서버 인증서를 신뢰할 수 없다면, 서버도 신뢰할 수 없기 때문에, 안전한 방법이라고는 할 수 없으나, 널리 알려진 해결책이다.
두 번째는 HttpURLConnection 에서 유례된 것으로 생각되는데, 여기서는 TrustManager 따위를 구현하는 것보다 간이한 방법에 대해 소개하려고 한다.
여기서 소개한 방법도 안전한 방법이라고 할 수 없으나, TrustManager 따위를 구현할 요량이라면, 검토할 가치가 있다.
1.4. 참고자료
다음에서 아래 쪽의 Properties 항목을 참조한다.
- com.sun.mail.imap (JavaMail API documentation)
- com.sun.mail.pop3 (JavaMail API documentation)
- com.sun.mail.smtp (JavaMail API documentation)
2. 이메일 서버에 SSL 로 접속할 때, 인증서 오류를 무시하는 방법
여기서 소개하는 방법은 다음과 같이 신뢰할 수 있는 host 를 전체(*)로 설정하는 방법이다.
java.util.Properties props = new java.util.Properties(); props.setProperty("mail.pop3.ssl.trust", "*"); javax.mail.Session session = javax.mail.Session.getInstance(props, null);
imap 은 다음과 같다.
props.setProperty("mail.imap.ssl.trust", "*");
smtp 는 다음과 같다.
props.setProperty("mail.smtps.ssl.trust", "*");
3. 전체 소스
3.1. pop3 혹은 imap
FetchMailProcessorImpl.java 에서 pop3/imap 에 연결하는 부분만 떼어낸 소스코드이다.
String type = "pop3"; //String type = "imap"; String host = ""; int port = 0; String userName = ""; String password = ""; String encryptionType = "STARTTLS"; //String encryptionType = "SSL/TSL"; java.util.Properties props = new java.util.Properties(); props.setProperty("mail.store.protocol", type); if(type.equals("pop3")) { props.setProperty("mail.pop3.socketFactory.class", "javax.net.ssl.SSLSocketFactory"); props.setProperty("mail.pop3.socketFactory.fallback", "false"); props.setProperty("mail.pop3.socketFactory.port", Integer.toString(port)); props.setProperty("mail.pop3.ssl.trust", "*"); props.setProperty("mail.pop3.port", Integer.toString(port)); } else if(type.equals("imap")) { props.setProperty("mail.imap.socketFactory.class", "javax.net.ssl.SSLSocketFactory"); props.setProperty("mail.imap.socketFactory.fallback", "false"); props.setProperty("mail.imap.socketFactory.port", Integer.toString(port)); props.setProperty("mail.imap.ssl.trust", "*"); if(encryptionType.equals("STARTTLS")) { props.setProperty("mail.imap.starttls.enable", "true"); } else if(encryptionType.equals("SSL/TSL")) { props.setProperty("mail.imap.ssl.enable", "true"); } props.setProperty("mail.imap.fetchsize", Integer.toString(1024 *1024)); props.setProperty("mail.imap.port", Integer.toString(port)); } javax.mail.URLName urln = new javax.mail.URLName( type, host, port, null, userName, password ); javax.mail.Session session = javax.mail.Session.getInstance(props, null); try { javax.mail.Store store = session.getStore(urln); /* store.connect(); store.close(); */ } catch (javax.mail.NoSuchProviderException e) { }
3.2. smtp
MailSendProcessorImpl.java 에서 smtps 에 연결하는 부분만 떼어낸 소스코드이다.
String type = "smtps"; String host = ""; int port = 0; String userName = ""; String password = ""; String encryptionType = "STARTTLS"; //String encryptionType = "SSL/TSL"; java.util.Properties props = new java.util.Properties(); props.setProperty("mail.smtps.socketFactory.class", "javax.net.ssl.SSLSocketFactory"); props.setProperty("mail.smtps.socketFactory.fallback", "false"); props.setProperty("mail.smtps.socketFactory.port", Integer.toString(port)); props.setProperty("mail.smtps.ssl.trust", "*"); if(encryptionType.equals("STARTTLS")) { props.setProperty("mail.smtps.starttls.enable", "true"); } else if(encryptionType.equals("SSL/TSL")) { props.setProperty("mail.smtps.ssl.enable", "true"); } props.setProperty("mail.smtps.port", Integer.toString(port)); javax.mail.URLName urln = new javax.mail.URLName( type, host, port, null, userName, password ); javax.mail.Session session = javax.mail.Session.getInstance(props, null); javax.mail.Transport transport = new com.sun.mail.smtp.SMTPTransport(session, urln); //transport.connect(); //transport.close();