Question

I'm having a weird problem with a JSF application I'm currently working on. It seems like there are two parts of my programm which are colliding.

There are two parts:

  • the "banking" functionality
  • the mail functionality

The relevant part of the banking functionality (It's a fake bank just for this exercise):

String path = FacesContext.getCurrentInstance().getExternalContext()                .getRealPath("/") + "/WEB-INF/sec/certs.jks";

ErrorHandler.trace(path);

System.setProperty("javax.net.ssl.trustStore", path);

Here it's setting the Trust Store with the certificate for the bank server.

The mail part looks like this:

Properties props = new Properties();
props.put("mail.smtp.auth", this.smtpServer.isAuthenticated());
props.put("mail.smtp.starttls.enable", this.smtpServer.isTls());
props.put("mail.smtp.host", this.smtpServer.getHostaddr());
props.put("mail.smtp.port", this.smtpServer.getPort());
props.put("mail.smtps.auth", "true");
props.put("mail.smtp.debug", "true");

final String username = this.smtpServer.getUsername();
final String password = this.smtpServer.getPassword();

Session session = Session.getDefaultInstance(props,
        new javax.mail.Authenticator() {
            @Override
            protected PasswordAuthentication getPasswordAuthentication() {
                return new PasswordAuthentication(username, password);
            }
        });

session.setDebug(true);

One way to reproduce the problem:

The problem I'm facing is that if I start the application and, for example, use the "change mail" functionality I'll get my notification mail immediately. No problem there. Then I'll try to buy a product thus triggering the bank functionality.

That's where the problem shows up:

Communication Error: javax.ws.rs.WebApplicationException: javax.xml.bind.MarshalException
 - with linked exception:
[javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target]

Another way to reproduce the problem:

Now let's say I restart my application and try to order something, this time it'll work but the mail functionality is broken with this error message:

DEBUG: setDebug: JavaMail version 1.4.7
DEBUG: getProvider() returning javax.mail.Provider[TRANSPORT,smtps,com.sun.mail.smtp.SMTPSSLTransport,Oracle]
DEBUG SMTP: useEhlo true, useAuth true
DEBUG SMTP: trying to connect to host "smtp.gmail.com", port 465, isSSL true
TRACE Error Could not connect to SMTP host: smtp.gmail.com, port: 465

Bottom line:

If I trigger bank and then mail -> mail not working If I trigger mail and then bank -> bank not working

Anyone who could find a problem there?

Thank you!

Was it helpful?

Solution

Your "banking functionality" is changing the trust store. That new trust store needs to have the certificates necessary to validate the SSL connection with your mail server. You can initialize your trust store with all the CA certificates from the JDK default trust store, or you can add just the specific certificate for your mail server - see the InstallCert program. Finally, you can configure JavaMail to use a separate trust store, or change your banking functionality to use a trust store explicitly rather than overriding the default trust store; those are probably more complicated.

OTHER TIPS

The problem was, that the mail functionality was working if there's no trustStore set (because it's using the system's default trustStore which is located in:

/Library/Java/JavaVirtualMachines/jdk1.7.0_25.jdk/Contents/Home/jre/lib/security/cacerts

on a Mac.

The banking functionality is using it's own certificate which was located in:

MyProject/.../WEB-INF/sec/certs.jks

Every time the JavaMail tried to authenticate to Google's SMTP server it tried to use the certs.jks trustStore even though I unset the trustStore property the banking functionality set in the mail method.

Fix:

At the beginning of the mail method:

String path = FacesContext.getCurrentInstance().getExternalContext()
            .getRealPath("/")
            + "WEB-INF/sec/certs.jks";
System.setProperty("javax.net.ssl.trustStore", path);

Import the default cacerts keyStore into our own custom keyStore:

keytool -importkeystore -srckeystore certs.jks -destkeystore cacerts
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top