Search code examples
tomcatssl-certificateclassloaderjakarta-mailsocketfactory

How to send emails from tomcat web application via exchange 2007 SMTP relay with invalid ssl certificate?


The problem I'm trying to solve is to send emails from my tomcat web application through an Exchange 2007 SMTP relay.

The exchange server does not have a valid SSL certificate installed, so I'm trying to push my own ssl socket factory which also accepts invalid certificates.

With the help of stack overflow I found a solution that works in a simple test program, but not when executed from within the running web app (class loading problem).

My current explanation is that the classloader of the SocketFetcher class is not able to see classes of my webapp, is that correct?

How could I workaround this limitation?

Just for the fun of it I put my factory in a separate jar and placed it in the tomcat lib folder, but it is still not found.

I get this exception:

Caused by: java.lang.ClassNotFoundException: com.mypackage.MyTrustAllSslSocketFactory
    at java.net.URLClassLoader$1.run(URLClassLoader.java:366)
    at java.net.URLClassLoader$1.run(URLClassLoader.java:355)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.net.URLClassLoader.findClass(URLClassLoader.java:354)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:423)
    at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:308)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:356)
    at javax.net.ssl.SSLSocketFactory.getDefault(SSLSocketFactory.java:104)
    at com.sun.mail.util.SocketFetcher.startTLS(SocketFetcher.java:247)
    ... 25 more

I push the factory like this:

Security.setProperty("ssl.SocketFactory.provider", MyTrustAllSslSocketFactory.class.getName());

My custom socket factory:

public class MyTrustAllSslSocketFactory extends SSLSocketFactory
{  
  private SSLSocketFactory    m_sslSocketFactory;

  public MyTrustAllSslSocketFactory() throws Exception
  {
    SSLContext sslContext = SSLContext.getInstance("TLS");
    sslContext.init(null, new TrustManager[] { new TrustAllX509Manager() }, new SecureRandom());
    m_sslSocketFactory = sslContext.getSocketFactory();
  }

  @Override
  public Socket createSocket(Socket s, String host, int port, boolean autoClose) throws IOException
  {
    return m_sslSocketFactory.createSocket(s, host, port, autoClose);
  }

  @Override
  public String[] getDefaultCipherSuites()
  {
    return m_sslSocketFactory.getDefaultCipherSuites();
  }

  @Override
  public String[] getSupportedCipherSuites()
  {
    return m_sslSocketFactory.getSupportedCipherSuites();
  }

  @Override
  public Socket createSocket(String host, int port) throws IOException, UnknownHostException
  {
    return m_sslSocketFactory.createSocket(host, port);
  }

  @Override
  public Socket createSocket(InetAddress host, int port) throws IOException
  {
    return m_sslSocketFactory.createSocket(host, port);
  }

  @Override
  public Socket createSocket(String host, int port, InetAddress localHost, int localPort) throws IOException, UnknownHostException
  {
    return m_sslSocketFactory.createSocket(host, port, localHost, localPort);
  }

  @Override
  public Socket createSocket(InetAddress address, int port, InetAddress localAddress, int localPort) throws IOException
  {
    return m_sslSocketFactory.createSocket(address, port, localAddress, localPort);
  }
}

And the "trust all" manager:

public class TrustAllX509Manager implements X509TrustManager
{
  @Override
  public X509Certificate[] getAcceptedIssuers()
  {
    return null;
  }

  @Override
  public void checkClientTrusted(X509Certificate[] certs, String authType)
  {
    // nothing
  }

  @Override
  public void checkServerTrusted(X509Certificate[] certs, String authType)
  {
    // nothing
  }
}

Solution

  • You've made this unnecessarily complicated.

    A simpler approach is to use the com.sun.mail.util.MailSSLSocketFactory class that comes with JavaMail. Create an instance of that class, configure it, and set it as the value of the mail.smtps.ssl.socketFactory Session property (or mail.smtp.ssl.socketFactory property if you're just using the "smtp" protocol and enabling STARTTLS support).