GriffoGoes
GriffoGoes

Reputation: 734

Available ciphers web application

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

Answers (2)

GriffoGoes
GriffoGoes

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:

  • 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

Upvotes: 0

Keyboardsoldier
Keyboardsoldier

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

Related Questions