Reputation: 734
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?
Upvotes: 0
Views: 928
Reputation: 734
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:
Steps used to find out:
Executing java application with the following flags:
-verbose:class
-verbose:jni
And checking the logs:
[Loaded com.sun.mail.smtp.SMTPTransport from file:/var/lib/tomcat8/webapps/myapp/WEB-INF/lib/javax.mail-1.6.1.jar]
[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
Upvotes: 0
Reputation: 1
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!
Upvotes: 0