0

This java application needs to send email using SMTP and TLS.

The mail sending code works fine when executed in a standalone java app (java MailClient) and in our previous environment, but fails when executed inside Tomcat 8 environment (Tomcat 8 AWS Beanstalk, OpenJDK 64-Bit Server VM (build 25.171-b10, mixed mode)).

Relevant mail sending code (using javax 1.6.1):

Properties props = new Properties();
props.put("mail.transport.protocol", "smtp");
props.put("mail.smtp.host", smtpServerProperties.get(HOST_PROPERTY));
props.put("mail.smtp.port", smtpServerProperties.get(PORT_PROPERTY));
props.put("mail.smtp.auth", "true");
props.put("mail.smtp.starttls.enable", "true");
Authenticator auth = new Authenticator() {
    protected PasswordAuthentication getPasswordAuthentication() {
        return new PasswordAuthentication(smtpServerProperties.get(TLS_USER_PROPERTY), smtpServerProperties.get(TLS_PASSWORD_PROPERTY));
    }
};
Session session = Session.getInstance(props, auth);
MimeMessage email = createEmail(session, smtpSender, recipients, subject, content);
Transport transport = session.getTransport();
transport.connect(smtpServerProperties.get(HOST_PROPERTY),smtpServerProperties.get(TLS_USER_PROPERTY), smtpServerProperties.get(TLS_PASSWORD_PROPERTY) );
transport.sendMessage(email, email.getAllRecipients());

Error that I get:

Caused by: java.io.EOFException: SSL peer shut down incorrectly
    at sun.security.ssl.InputRecord.read(InputRecord.java:505)
    at sun.security.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:983)
    ... 13 more

And I suspect the problem is that somehow the available ciphers for the app when running "inside" tomcat is limited, and then it fails. In the SSL logs I can see the list of 'unsupported cipher suite' is very different between applications as well as the initial TLS version used for the handshake:

Inside tomcat:

Ignoring unsupported cipher suite: TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384 for TLSv1
Ignoring unsupported cipher suite: TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384 for TLSv1
Ignoring unsupported cipher suite: TLS_RSA_WITH_AES_256_CBC_SHA256 for TLSv1
Ignoring unsupported cipher suite: TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384 for TLSv1
Ignoring unsupported cipher suite: TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384 for TLSv1
Ignoring unsupported cipher suite: TLS_DHE_RSA_WITH_AES_256_CBC_SHA256 for TLSv1
Ignoring unsupported cipher suite: TLS_DHE_DSS_WITH_AES_256_CBC_SHA256 for TLSv1
Ignoring unsupported cipher suite: TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384
Ignoring unsupported cipher suite: TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384
Ignoring unsupported cipher suite: TLS_RSA_WITH_AES_256_CBC_SHA256
Ignoring unsupported cipher suite: TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384
Ignoring unsupported cipher suite: TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384
Ignoring unsupported cipher suite: TLS_DHE_RSA_WITH_AES_256_CBC_SHA256
Ignoring unsupported cipher suite: TLS_DHE_DSS_WITH_AES_256_CBC_SHA256
Ignoring unsupported cipher suite: TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256
Ignoring unsupported cipher suite: TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256
Ignoring unsupported cipher suite: TLS_RSA_WITH_AES_128_CBC_SHA256
Ignoring unsupported cipher suite: TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256
Ignoring unsupported cipher suite: TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256
Ignoring unsupported cipher suite: TLS_DHE_RSA_WITH_AES_128_CBC_SHA256
Ignoring unsupported cipher suite: TLS_DHE_DSS_WITH_AES_128_CBC_SHA256
Ignoring unsupported cipher suite: TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384
Ignoring unsupported cipher suite: TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256
Ignoring unsupported cipher suite: TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
Ignoring unsupported cipher suite: TLS_RSA_WITH_AES_256_GCM_SHA384
Ignoring unsupported cipher suite: TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384
Ignoring unsupported cipher suite: TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384
Ignoring unsupported cipher suite: TLS_DHE_RSA_WITH_AES_256_GCM_SHA384
Ignoring unsupported cipher suite: TLS_DHE_DSS_WITH_AES_256_GCM_SHA384
Ignoring unsupported cipher suite: TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
Ignoring unsupported cipher suite: TLS_RSA_WITH_AES_128_GCM_SHA256
Ignoring unsupported cipher suite: TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256
Ignoring unsupported cipher suite: TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256
Ignoring unsupported cipher suite: TLS_DHE_RSA_WITH_AES_128_GCM_SHA256
Ignoring unsupported cipher suite: TLS_DHE_DSS_WITH_AES_128_GCM_SHA256
%% No cached client session
*** ClientHello, TLSv1

Outside:

Ignoring unsupported cipher suite: TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384 for TLSv1
Ignoring unsupported cipher suite: TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384 for TLSv1
Ignoring unsupported cipher suite: TLS_RSA_WITH_AES_256_CBC_SHA256 for TLSv1
Ignoring unsupported cipher suite: TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384 for TLSv1
Ignoring unsupported cipher suite: TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384 for TLSv1
Ignoring unsupported cipher suite: TLS_DHE_RSA_WITH_AES_256_CBC_SHA256 for TLSv1
Ignoring unsupported cipher suite: TLS_DHE_DSS_WITH_AES_256_CBC_SHA256 for TLSv1
Ignoring unsupported cipher suite: TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384 for TLSv1.1
Ignoring unsupported cipher suite: TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384 for TLSv1.1
Ignoring unsupported cipher suite: TLS_RSA_WITH_AES_256_CBC_SHA256 for TLSv1.1
Ignoring unsupported cipher suite: TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384 for TLSv1.1
Ignoring unsupported cipher suite: TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384 for TLSv1.1
Ignoring unsupported cipher suite: TLS_DHE_RSA_WITH_AES_256_CBC_SHA256 for TLSv1.1
Ignoring unsupported cipher suite: TLS_DHE_DSS_WITH_AES_256_CBC_SHA256 for TLSv1.1
%% No cached client session
*** ClientHello, TLSv1.2

EDIT

The same application, running in Tomcat, can successfully talk to mongoDb servers (mongo-java-driver-3.2.2) and to other web applications with https (standard HttpURLConnection), and in both cases the ClientHello uses TLSv1.2.

The question would be: How is the tomcat environment limiting/changing the available ciphers for the javax.mail, even if it (really seems to be) is using the same java environment?

GriffoGoes
  • 715
  • 7
  • 17
  • Noticed you client said TLSv1 vs TLSv1.2, https://stackoverflow.com/questions/28908835/ssl-peer-shut-down-incorrectly-in-java – Compass Jul 10 '18 at 20:16
  • 1
    Are you sure it's using the same Java environment? Looks like a different one to me. Tomcat doesn't care about client SSL cipher suites, or SSL clients at all actually. – user207421 Jul 11 '18 at 00:06
  • 1
    The longer list of 'unsupported' ciphers 'inside' are all suites that require TLS1.2, and can't be used in 1.0 which it is using as @Compass notes (or 1.1). This would be consistent with using Java 7 which defaults 1.2 and 1.1 on for JSSE server but off for client, but 25.171 should be Java 8. Also, both lists exclude apparently all AES256 suites which is consistent with Oracle/Sun Java until recently not having added the 'unlimited strength' policy (you can search many other Qs on this), but that _usually_ does not apply to OpenJDK. (Maybe Amazon added it?) – dave_thompson_085 Jul 11 '18 at 02:27
  • Just checked and even though the instance had openjdk 7 also installed, it wasn't used. I yum uninstalled it and manually removed the folders and still had the same error. And on second thought, I don't think it is just cipher issue overall, as the same application can successfully talk to mongo servers (TLSv1.2) and to other applications (using https and TLSv1.2 shows). Will update the question – GriffoGoes Jul 11 '18 at 12:12
  • As others have suggested, this is most likely due to use of different JDK versions and their different SSL defaults. Note that you can override the default for JavaMail by setting the [`mail.smtp.ssl.protocols`](https://javaee.github.io/javamail/docs/api/com/sun/mail/smtp/package-summary.html#mail.smtp.ssl.protocols) and [`mail.smtp.ssl.ciphersuites`](https://javaee.github.io/javamail/docs/api/com/sun/mail/smtp/package-summary.html#mail.smtp.ssl.ciphersuites) properties – Bill Shannon Jul 11 '18 at 19:17

2 Answers2

0

It certainly could be the ciphers. It's definitely because your SSL handshake is failing, but there's a lot of reasons why that can happen.

Because you didn't see the issue until the app was hosted in Tomcat suggests you might be dealing with an SSL trust-store problem. Get the SSL cert served up by your SMTP server's SSL endpoint and use the (java/bin) keytool to import that cert into Tomcat's trusted keystore. If it's already in there, you'll get a message that looks like "It's already in here, do you want to re-import it?"

Re-importing it won't hurt, but trust-stores typically don't get loaded dynamically so after you do that trust-store import you should restart Tomcat. you can type (java/bin) keytool without parameters to get a help screen, but the import command looks something like this:

keytool -import -file smtpcert.cer -keystore -tomcattrust

Your filename (.cer) and trust store name may be different from above, and if prompted for a password I think the default one is changeit.

Bon chance!

  • 1
    Lack of trust in the server's cert would cause a `ValidatorException`, not ever 'peer shut down incorrectly', so this can't be the problem. If it were, trusting the leaf cert is usually the wrong solution. – dave_thompson_085 Jul 11 '18 at 02:29
  • As @dave_thompson_085 noted, the lack of trust would give other error and in later stages of the handshake (had a few those in different scenarios already) – GriffoGoes Jul 11 '18 at 11:44
0

tldr: the full web application had a dependency including an old version of the mail class (1.4) and it didn't work. Fix was to add an exclusion in the pom.file:

<dependency>
    <groupId>org.jboss.resteasy</groupId>
    <artifactId>resteasy-multipart-provider</artifactId>
    <version>2.2.1.GA</version>
    <exclusions>
        <exclusion>
            <groupId>javax.mail</groupId>
            <artifactId>mail</artifactId>
        </exclusion>
    </exclusions>
</dependency>

Reasons this took way longer than expected to figure it out:

  • the same web application worked in a (totally) different environment - windows server, Sun JDK8, Tomcat7
  • The email-sending class was in a "core" project, which did not include any other mail dependency
  • Initial suspect was a different class/implementation was being used in the "inside" and "outside" version, but they were the "same" (name at least)

Steps used to find out:

Executing java application with the following flags:

-verbose:class
-verbose:jni

And checking the logs:

  • Standalone app (when running my "main" class, I used the libs from the deployment of the web) : [Loaded com.sun.mail.smtp.SMTPTransport from file:/var/lib/tomcat8/webapps/myapp/WEB-INF/lib/javax.mail-1.6.1.jar]
  • Web app: [Loaded com.sun.mail.smtp.SMTPTransport from file:/var/lib/tomcat8/webapps/myapp/WEB-INF/lib/mail-1.4.jar]

Final validation based on running a mvn dependency mvn dependency:tree and seeing two mail dependencies:

[INFO] +- com.my.company:my-core:jar:1.70:compile
[INFO] |  \- com.sun.mail:javax.mail:jar:1.6.1:compile
...
[INFO] +- org.jboss.resteasy:resteasy-multipart-provider:jar:2.2.1.GA:compile
[INFO] |  +- javax.mail:mail:jar:1.4:compile
GriffoGoes
  • 715
  • 7
  • 17