0

I have a method that returns a session. My goal is that it always should return a new session when the method is called. I have a web application and when I click on "Start" or "Send" then it always connects and sends a mail.

I have following cases:

  1. I connect() to a server, which needs my own custom truststore.
  2. If I connect to this server, I should select truststorename.jks from the dropdown box. And it should work. If I do not select my truststore, then an exception should be thrown.
  3. If I connect to another server, which doesnt need my own custom truststore, then I can select "None". So java takes the default configuration.

The exception that should be thrown is at the Transport.connect() method of javamail:

java.security.InvalidAlgorithmParameterException: the trustAnchors parameter must be non-empty

The problem:

Now when I start or restart the webapplication - it works only for the first time.

So if I select my own truststore, it works with no exception. But then I select "Nothing" or "None" from the dropdown box but it still sends successfully instead of throwing an exception. So I think that the old session is still there and a new one will only be created when I redeploy the web-app. Maybe there is a conflict with the System-Properties? Or jetty?

    private Session connect(SMTPTask task) {

    log.debug("Connecting to SMTP");
    Properties props = new Properties();
    Properties systemProps = System.getProperties();

    if (task.getKeyStorePath().contains("None")) {
        systemProps.remove("javax.net.ssl.trustStore");
        systemProps.remove("javax.net.ssl.trustStorePassword");
        System.setProperties(systemProps);
        props.remove("mail.smtp.ssl.socketFactory");
    } else {

        systemProps.put("javax.net.ssl.trustStore", "src/main/resources/stores/" + task.getKeyStorePath());
        systemProps.put("javax.net.ssl.trustStorePassword", "changeit");
        System.setProperties(systemProps);

        SSLSocketFactory factory = null;
        try {
            SSLContext ctx;
            KeyManagerFactory kmf;
            KeyStore ks;
            char[] passphrase = "changeit".toCharArray();

            ctx = SSLContext.getInstance("TLS");
            kmf = KeyManagerFactory.getInstance("SunX509");
            ks = KeyStore.getInstance("JKS");

            URL resource = this.getClass().getResource("/stores/" + task.getKeyStorePath());
            File file = new File(resource.toURI());

            FileInputStream fis = new FileInputStream(file);
            ks.load(fis, passphrase);
            fis.close();
            kmf.init(ks, passphrase);
            ctx.init(kmf.getKeyManagers(), null, null);

            factory = ctx.getSocketFactory();

            props.put("mail.smtp.ssl.socketFactory", factory);
        } catch (Exception e) {
            log.error("Error with SSLFactory", e);
        }
    }

    String host = task.getHost();
    String port = String.valueOf(task.getPort());

    props.put("mail.smtps.host", host);
    props.put("mail.smtps.port", port);
    props.put("mail.smtps.auth", "true");
    props.put("mail.imaps.fetchsize", "22020096");

    return Session.geInstance(props);
    }

Somewhere else I do:

public void send() {
   ...
   Session s = connect(task);
   ...
}

EDIT:

I am not trying to do it with MailSSLFactory:

private Session connect(SMTPTask task) {

    ...

        try {
            MailSSLSocketFactory factory = new MailSSLSocketFactory();
            KeyManagerFactory kmf= KeyManagerFactory.getInstance("SunX509");
            KeyStore ks = KeyStore.getInstance("JKS");
            char[] passphrase = "changeit".toCharArray();

            URL resource = this.getClass().getResource("/stores/" + task.getKeyStorePath());
            File file = new File(resource.toURI());

            FileInputStream fis = new FileInputStream(file);
            ks.load(fis, passphrase);
            fis.close();
            kmf.init(ks,passphrase);
            factory.setKeyManagers(kmf.getKeyManagers());
       factory.setTrustAllHosts(true);

            props.put("mail.smtp.ssl.socketFactory", factory);
        } catch (Exception e) {
            log.error("Error with SSLFactory", e);
        }
    }

    props.put("mail.smtps.host", host);
    props.put("mail.smtps.port", port);
    props.put("mail.smtps.auth", "true");
    props.put("mail.imaps.fetchsize", "22020096");

    return Session.getInstance(props);
}
David Sonnenfeld
  • 688
  • 5
  • 17
  • 31

1 Answers1

0

What do you expect to throw the exception when you select "None"? Certainly Session.getInstance isn't going to throw an exception. Perhaps the properties you've configured for the Session won't actually work, but you won't find that out until you try to use it to connect to your mail server.

Bill Shannon
  • 29,579
  • 6
  • 38
  • 40
  • yes, there is one server that needs my own custom truststore... So when I select the name of the truststore in the dropdown it works, like it should... when I change to "none" of course an exception should be thrown ;) – David Sonnenfeld Feb 26 '13 at 02:33
  • Should be thrown by what method call? – Bill Shannon Feb 26 '13 at 08:02
  • Either at Session.getInstance() or Transport.connect().. both from javamail... He throws the exception successfully, when I select "None" at the beginning. But after that he always throws the exception. Doesnt matter if I select "none" or "mytruststore.jks" – David Sonnenfeld Feb 26 '13 at 22:44
  • Session.getInstance isn't going to throw the exception. Transport.connect will use the configuration in the Session, and that's when it will determine if it works or not. Note that your code is changing System properties, which are essentially global variables. Even if you have a Session that was configured properly and worked once, calling this code again with "None" will change the System properties and prevent the previous Session from working. Perhaps that's your problem? – Bill Shannon Feb 27 '13 at 19:24
  • You should look at the [javadocs for the com.sun.mail.util.MailSSLSocketFactory class](http://javamail.kenai.com/nonav/javadocs/com/sun/mail/util/MailSSLSocketFactory.html), which you can configure on a per-Session basis, and avoid using System properties. – Bill Shannon Feb 27 '13 at 19:25
  • Yes, I tried with MailSSLSocketFactory now, but I always get following Exception (I edited my post above): PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target – David Sonnenfeld Feb 28 '13 at 10:39
  • Additionally the javamail doc says: For custom certificates: "The InstallCert program will help." But I want to avoid installing this. It should go through java. Thats why I used System Properties. – David Sonnenfeld Feb 28 '13 at 10:52
  • You're setting the socket factory using the mail.smtp.* property, but elsewhere in your program you're setting mail.smtps.* properties. Are you using the "smtp" or "smtps" protocol to connect to the server? – Bill Shannon Feb 28 '13 at 22:54
  • So set the socket factory using the mail.smtps.* property. – Bill Shannon Mar 01 '13 at 18:26
  • I found the solution in the following post: http://stackoverflow.com/questions/1828775/how-to-handle-invalid-ssl-certificates-with-apache-httpclient – David Sonnenfeld Mar 01 '13 at 23:11
  • Well, there's a ton of information in that post, some of it good, most of it unnecessary if you use MailSSLSocketFactory. Were you not able to figure out how to set the properties to get MailSSLSocketFactory to work? – Bill Shannon Mar 03 '13 at 08:23
  • the mail.smtps.* did not work, if I remember correctly... so I did http://pastebin.com/0eUzWkB8 – David Sonnenfeld Mar 04 '13 at 00:27
  • That code is using the mail.smtp.* property, so I guess you're not really using the "smtps" protocol. Anyway, if you'd like help getting MailSSLScoketFactory to work, let me know. – Bill Shannon Mar 04 '13 at 19:31