2011年1月23日 星期日

Javamail over SSL and TLS

最近因為工作得需要,把原本的javamail連線改為ssl,就順便把遭遇到的問題及解法附上來
網路上最多的javamail ssl範例為gmail的寄信範例,其中又分為SSL與TLS,GMAIL的SSL範例參考如下
http://www.mkyong.com/java/javamail-api-sending-email-via-gmail-smtp-example/



import java.util.Properties;
import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.PasswordAuthentication;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;

public class JavaMailApp2
{
public static void main( String[] args )
{
Properties props = new Properties();
props.put("mail.smtp.host", "smtp.gmail.com");
props.put("mail.smtp.socketFactory.port", "465");
props.put("mail.smtp.socketFactory.class","javax.net.ssl.SSLSocketFactory");
props.put("mail.smtp.auth", "true");
props.put("mail.smtp.port", "465");

Session session = Session.getDefaultInstance(props,
new javax.mail.Authenticator()
{
protected PasswordAuthentication getPasswordAuthentication()
{ return new PasswordAuthentication("username","password"); }
});

try {

Message message = new MimeMessage(session);
message.setFrom(new InternetAddress("from@no-spam.com"));
message.setRecipients(Message.RecipientType.TO,
InternetAddress.parse("to@no-spam.com"));
message.setSubject("Testing Subject");
message.setText("test msg");

Transport.send(message);

System.out.println("Done");

} catch (MessagingException e) {
throw new RuntimeException(e);
}
}
}

而在TLS的傳輸上可以參考http://www.coderanch.com/t/471285/sockets/java/Confusion-over-SSL-TLS-sending 的範例,這裡附上hotmail與gmail的設定
Hotmail:

Security.addProvider(new com.sun.net.ssl.internal.ssl.Provider());
Properties props = new Properties();
props.put("mail.host","smtp.live.com");
props.put("mail.smtp.port", 587);
props.put("mail.smtp.auth", "true");

Gmail:

Security.addProvider(new com.sun.net.ssl.internal.ssl.Provider());
Properties props = new Properties();
props.put("mail.host","smtp.gmail.com");
props.put("mail.smtp.port", 587);
props.put("mail.smtp.auth", "true");

上面的設定也實際測試過驗證無誤,此類連線為server端設定以TLS加密傳送純文字格式密碼做為驗證(Plaintext Authentication over TLS),如下圖:


不過特別要注意的是雖然爬完網路上的文章,一開始依然沒辦法成功的建立連線,出現下列的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
原因為寄信程式得client端並未找到信任server端的truststore,是故跟mail server管理者取得.cer檔的憑證後,使用keytool將該cer檔封裝到jks的truststore conntainer中,並且於程式碼指定truststore位置

System.setProperty("javax.net.ssl.trustStore","C:/trustStore.jks");

如此才成功的使用TLS連線發送信件

沒有留言: