Using JavaMail with TLS

JavaSmtpJakarta MailSsl

Java Problem Overview


I found several other questions on SO regarding the JavaMail API and sending mail through an SMTP server, but none of them discussed using TLS security. I'm trying to use JavaMail to send status updates to myself through my work SMTP mail server, but it requires TLS, and I can't find any examples online of how to use JavaMail to access an SMTP server that requires TLS encryption. Can anyone help with this?

Java Solutions


Solution 1 - Java

We actually have some notification code in our product that uses TLS to send mail if it is available.

You will need to set the Java Mail properties. You only need the TLS one but you might need SSL if your SMTP server uses SSL.

Properties props = new Properties();
props.put("mail.smtp.starttls.enable","true");
props.put("mail.smtp.auth", "true");  // If you need to authenticate
// Use the following if you need SSL
props.put("mail.smtp.socketFactory.port", d_port);
props.put("mail.smtp.socketFactory.class", "javax.net.ssl.SSLSocketFactory");
props.put("mail.smtp.socketFactory.fallback", "false");

You can then either pass this to a JavaMail Session or any other session instantiator like Session.getDefaultInstance(props).

Solution 2 - Java

Good post, the line

props.put("mail.smtp.socketFactory.class", "javax.net.ssl.SSLSocketFactory");

is mandatory if the SMTP server uses SSL Authentication, like the GMail SMTP server does. However if the server uses Plaintext Authentication over TLS, it should not be present, because Java Mail will complain about the initial connection being plaintext.

Also make sure you are using the latest version of Java Mail. Recently I used some old Java Mail jars from a previous project and could not make the code work, because the login process was failing. After I have upgraded to the latest version of Java Mail, the reason of the error became clear: it was a javax.net.ssl.SSLHandshakeException, which was not thrown up in the old version of the lib.

Solution 3 - Java

The settings from the example above didn't work for the server I was using (authsmtp.com). I kept on getting this error:

javax.net.ssl.SSLException: Unrecognized SSL message, plaintext connection?

I removed the mail.smtp.socketFactory settings and everything worked. The final settings were this (SMTP auth was not used and I set the port elsewhere):

java.util.Properties props = new java.util.Properties();
props.put("mail.smtp.starttls.enable", "true");

Solution 4 - Java

Just use the following code. It is really useful to send email via Java, and it works:

import java.util.*;
import javax.activation.CommandMap;
import javax.activation.MailcapCommandMap;
import javax.mail.*;
import javax.mail.Provider;
import javax.mail.internet.*;

public class Main {

	public static void main(String[] args) {
            final String username="[email protected]";
            final String password="password";
            Properties prop=new Properties();
            prop.put("mail.smtp.auth", "true");
            prop.put("mail.smtp.host", "smtp.gmail.com");
            prop.put("mail.smtp.port", "587");
            prop.put("mail.smtp.starttls.enable", "true");

            Session session = Session.getDefaultInstance(prop,
		  new javax.mail.Authenticator() {
			protected PasswordAuthentication getPasswordAuthentication() {
				return new PasswordAuthentication(username, password);
		  }
	    });
          try {
                 String body="Dear Renish Khunt Welcome";
                 String htmlBody = "<strong>This is an HTML Message</strong>";
                 String textBody = "This is a Text Message.";
		 Message message = new MimeMessage(session);
		 message.setFrom(new InternetAddress("[email protected]"));
		         message.setRecipients(Message.RecipientType.TO,InternetAddress.parse("[email protected]"));
		message.setSubject("Testing Subject");
        MailcapCommandMap mc = (MailcapCommandMap) CommandMap.getDefaultCommandMap();
        mc.addMailcap("text/html;; x-java-content-handler=com.sun.mail.handlers.text_html");
        mc.addMailcap("text/xml;; x-java-content-handler=com.sun.mail.handlers.text_xml");
        mc.addMailcap("text/plain;; x-java-content-handler=com.sun.mail.handlers.text_plain");
        mc.addMailcap("multipart/*;; x-java-content-handler=com.sun.mail.handlers.multipart_mixed");
        mc.addMailcap("message/rfc822;; x-java-content-handler=com.sun.mail.handlers.message_rfc822");
        CommandMap.setDefaultCommandMap(mc);
			message.setText(htmlBody);
                        message.setContent(textBody, "text/html");
			Transport.send(message);

			System.out.println("Done");

		} catch (MessagingException e) {
			e.printStackTrace();
		}

    }

}

Solution 5 - Java

I just spent quite a bit of time figuring out how to use JavaMail to send emails with gmail or office365. I couldn't find the answer in the faq, but was helped a bit by the javadoc.

If you forget props.put("mail.smtp.starttls.enable","true") you get com.sun.mail.smtp.SMTPSendFailedException: 451 5.7.3 STARTTLS is required to send mail.

If you forget props.put("mail.smtp.ssl.protocols", "TLSv1.2"); you get javax.mail.MessagingException: Could not convert socket to TLS; and No appropriate protocol (protocol is disabled or cipher suites are inappropriate). Apparently this is only necessary for JavaMail versions 1.5.2 and older.

Here is the minimal code I required to get it to work:

import java.util.Properties;
import javax.mail.Authenticator;
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 SendEmail {
    public static void main(String[] args) {
        Properties props = new Properties();
        props.put("mail.smtp.auth", "true");
        props.put("mail.smtp.starttls.enable","true");
        // smtp.gmail.com supports TLSv1.2 and TLSv1.3
        // smtp.office365.com supports only TLSv1.2
        props.put("mail.smtp.ssl.protocols", "TLSv1.2");
        props.put("mail.smtp.host", "smtp.office365.com");
        props.put("mail.smtp.port", "587");
        
        Session session = Session.getInstance(props, new Authenticator() {
            @Override
            protected PasswordAuthentication getPasswordAuthentication() {
                return new PasswordAuthentication("[email protected]", "******");
            }
        });
        
        try {
            Message message = new MimeMessage(session);
            message.setFrom(new InternetAddress("[email protected]"));
            message.setRecipients(Message.RecipientType.TO, InternetAddress.parse("[email protected]"));
            message.setSubject("You got mail");
            message.setText("This email was sent with JavaMail.");
            Transport.send(message);
            System.out.println("Email sent.");
        } catch (MessagingException e) {
            throw new RuntimeException(e);
        }
    }
}

Solution 6 - Java

With Simple Java Mail 5.0.0 (simplejavamail.org) it is very straightforward and the library will take care of all the Session properties for you.

Here's an example using Google's SMTP servers:

Email email = EmailBuilder.startingBlank()
        .from("lollypop", "[email protected]")
        .to("C.Cane", "[email protected]")
        .withSubject("hey")
        .withPlainText("We should meet up!")
        .withHTMLText("<b>We should meet up!</b>")
        .buildEmail();

MailerBuilder.withSMTPServer("smtp.gmail.com", 25, "user", "pass", SMTP_TLS)
        .buildMailer()
        .sendMail(email);

MailerBuilder.withSMTPServer("smtp.gmail.com", 587, "user", "pass", SMTP_TLS)
        .buildMailer()
        .sendMail(email);

MailerBuilder.withSMTPServer("smtp.gmail.com", 465, "user", "pass", SMTP_SSL)
        .buildMailer()
        .sendMail(email);

If you have two-factor login turned on, you need to generate an application specific password from your Google account.

Solution 7 - Java

As a small note, it only started to work for me when I changed smtp to smtps in the examples above per samples from javamail (see smtpsend.java, https://github.com/javaee/javamail/releases/download/JAVAMAIL-1_6_2/javamail-samples.zip, option -S).

My resulting code is as follow:

Properties props=new Properties();
props.put("mail.smtps.starttls.enable","true");
// Use the following if you need SSL
props.put("mail.smtps.socketFactory.port", port);
props.put("mail.smtps.socketFactory.class", "javax.net.ssl.SSLSocketFactory");
props.put("mail.smtps.socketFactory.fallback", "false");
props.put("mail.smtps.host", serverList.get(randNum));
Session session = Session.getDefaultInstance(props);

smtpConnectionPool = new SmtpConnectionPool(
    SmtpConnectionFactories.newSmtpFactory(session));
    
final ClosableSmtpConnection transport = smtpConnectionPool.borrowObject();
...
transport.sendMessage(message, message.getAllRecipients());

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestiondancavallaroView Question on Stackoverflow
Solution 1 - JavaSaratView Answer on Stackoverflow
Solution 2 - JavajasonsmithView Answer on Stackoverflow
Solution 3 - JavaBrent MatzelleView Answer on Stackoverflow
Solution 4 - JavaRenish KhuntView Answer on Stackoverflow
Solution 5 - JavaKristof NeirynckView Answer on Stackoverflow
Solution 6 - JavaBenny BottemaView Answer on Stackoverflow
Solution 7 - JavaEvgeniy NoskovView Answer on Stackoverflow