I've been testing some code for sending secure e-mail, and I've been using my Gmail account for testing. I've done a lot of googling on the subject of Java and secure SMTP access, and found a lot of specific example regarding Gmail in particular.
I've got code that works now, but I'm a bit puzzled. In order to make my code work, I MUST use port 465. I cannot get the code to work if I try to access Gmail using port 587, even though that's the recommended port and the one that I've always used to configure an e-mail client like Mac OS X Mail or Mozilla.
In the googling I've done, I've seen stories of success and failure, and I've seen examples with both ports used. The successful code seems to be using port 465, but I've never heard the particular port used mentioned as an important ingredient for success. In fact, to the minimal degree I've heard the port discussed at all, it's been to say "use either one, it doesn't matter."
This leaves me feeling that there's something missing in my code -- I'd think that if everything were in order, port 587 would also work for me. Further, while port 465 does work, in the debug trace I see "isSSL false", even though I'm specifying an SSL socket factory and am pretty sure that SSL is being used.
Here's a portion of the debug log where I fail using port 587:
...
DEBUG SMTP: useEhlo true, useAuth true
DEBUG SMTP: useEhlo true, useAuth true
DEBUG SMTP: trying to connect to host "smtp.gmail.com", port 587, isSSL false
DEBUG SMTP: exception reading response: javax.net.ssl.SSLException: Unrecognized SSL message, plaintext connection?
What's all this about a "plaintext connection"? Why does it say, both here and below when my connection succeeded "isSSL false"? I experimented with the SMTPSSLTransport class, which gave me the more comforting "isSSL true", but my results didn't change one bit.
Here's a portion of the debug log where I succeed using port 465:
...
DEBUG SMTP: useEhlo true, useAuth true
DEBUG SMTP: useEhlo true, useAuth true
DEBUG SMTP: trying to connect to host "smtp.gmail.com", port 465, isSSL false
220 mx.gmail.com ESMTP i15sm7669285wxd
DEBUG SMTP: connected to host "smtp.gmail.com", port: 465
EHLO myaccount
250-mx.gmail.com at your service
250-SIZE 20971520
250-8BITMIME
250-AUTH LOGIN PLAIN
250 ENHANCEDSTATUSCODES
...
And for any light it might shed on the situation, here's an edited-down version of my code:
public String sendMessage(Vector<String> recipients, String subject, String messageText)
{
String error = null;
if (subject == null)
subject = "(no subject)";
if (messageText == null)
messageText = "";
try {
Properties props = new Properties();
String from = settingsManager.getSetting(CONFIG_SMTP_FROM); // any ol' e-mail address
String host = settingsManager.getSetting(CONFIG_SMTP_HOST); // smtp.gmail.com
String port = settingsManager.getSetting(CONFIG_SMTP_PORT); // 465 or 587
String auth = settingsManager.getSetting(CONFIG_SMTP_AUTH); // "tls"
boolean doAuth = !"none".equals(auth);
boolean useTLS = "tls".equals(auth);
Session session;
props.put("mail.from", from);
props.put("mail.smtp.host", host);
props.put("mail.smtp.port", port);
props.put("mail.transport.protocol", "smtp");
//props.put("mail.debug", "true");
if (useTLS) {
props.put("mail.smtp.socketFactory.port", port);
props.put("mail.smtp.socketFactory.class", "javax.net.ssl.SSLSocketFactory");
props.put("mail.smtp.socketFactory.fallback", "false");
props.put("mail.smtp.starttls.enable", "true");
props.put("mail.smtp.ssl", "true");
}
if (doAuth) {
String username = settingsManager.getSetting(CONFIG_SMTP_USER);
String password = getEncryptedPassword();
Authenticator authenticator = new MyAuthenticator(username, password);
props.put("mail.user", username);
props.put("mail.smtp.auth", "true");
session = Session.getInstance(props, authenticator);
}
else
session = Session.getInstance(props);
MimeMessage message = new MimeMessage(session);
for (String addressee : recipients)
message.addRecipient(Message.RecipientType.TO, new InternetAddress(addressee));
message.setFrom(new InternetAddress(from));
message.setText(messageText);
message.setSubject(subject);
Transport.send(message);
}
catch (SendFailedException e) {
error = exceptionToErrorMessage(e);
}
catch (NoSuchProviderException e) {
error = exceptionToErrorMessage(e);
}
catch (AddressException e) {
error = exceptionToErrorMessage(e);
}
catch (MessagingException e) {
error = exceptionToErrorMessage(e);
}
return error;
}
...
private class MyAuthenticator extends Authenticator
{
private String username;
private String password;
public MyAuthenticator(String username, String password)
{
this.username = username;
this.password = password;
}
public PasswordAuthentication getPasswordAuthentication()
{
return new PasswordAuthentication(username, password);
}
}