bbottema/simple-java-mail

Add API for using custom SSLSocketFactory

t101jv opened this issue · 6 comments

Hi, our service is using this mailing library and we are getting the following integration error with our fips compliance configuration.

ERROR 2020-07-02 18:29:11,118 [pool-14-thread-4] com.oracle.pic.announcements.management.email.DynSmtpHealthCheck: dyn healthcheck failed
org.simplejavamail.mailer.internal.MailerException: Was unable to connect to SMTP server
at org.simplejavamail.mailer.internal.TestConnectionClosure.executeClosure(TestConnectionClosure.java:57)
at org.simplejavamail.mailer.internal.AbstractProxyServerSyncingClosure.run(AbstractProxyServerSyncingClosure.java:56)
at org.simplejavamail.mailer.internal.MailerImpl.testConnection(MailerImpl.java:303)
at org.simplejavamail.mailer.internal.MailerImpl.testConnection(MailerImpl.java:292)
at com.oracle.pic.announcements.management.email.SimpleMailSender.testConnection(SimpleMailSender.java:23)
at com.oracle.pic.announcements.management.email.DynSmtpHealthCheck.check(DynSmtpHealthCheck.java:26)
at com.codahale.metrics.health.HealthCheck.execute(HealthCheck.java:320)
at com.oracle.pic.announcements.common.health.DownstreamServicesHealthcheck$AsyncHealthCheck.lambda$new$0(DownstreamServicesHealthcheck.java:146)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
at java.util.concurrent.FutureTask.runAndReset(FutureTask.java:308)
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$301(ScheduledThreadPoolExecutor.java:180)
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:294)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at java.lang.Thread.run(Thread.java:748)
Caused by: javax.mail.MessagingException: Could not convert socket to TLS
at com.sun.mail.smtp.SMTPTransport.startTLS(SMTPTransport.java:2140)
at com.sun.mail.smtp.SMTPTransport.protocolConnect(SMTPTransport.java:734)
at javax.mail.Service.connect(Service.java:342)
at javax.mail.Service.connect(Service.java:222)
at javax.mail.Service.connect(Service.java:171)
at org.simplejavamail.mailer.internal.util.TransportRunner.runOnSessionTransport(TransportRunner.java:75)
at org.simplejavamail.mailer.internal.util.TransportRunner.connect(TransportRunner.java:60)
at org.simplejavamail.mailer.internal.TestConnectionClosure.executeClosure(TestConnectionClosure.java:54)
... 14 common frames omitted
Caused by: java.io.IOException: Can't create MailSSLSocketFactory
at com.sun.mail.util.SocketFetcher.startTLS(SocketFetcher.java:517)
at com.sun.mail.smtp.SMTPTransport.startTLS(SMTPTransport.java:2135)
... 21 common frames omitted
Caused by: java.security.KeyManagementException: FIPS mode: only SunJSSE TrustManagers may be used
at sun.security.ssl.SSLContextImpl.chooseTrustManager(SSLContextImpl.java:120)
at sun.security.ssl.SSLContextImpl.engineInit(SSLContextImpl.java:83)
at javax.net.ssl.SSLContext.init(SSLContext.java:282)
at com.sun.mail.util.MailSSLSocketFactory.newAdapteeFactory(MailSSLSocketFactory.java:109)
at com.sun.mail.util.MailSSLSocketFactory.(MailSSLSocketFactory.java:96)
at com.sun.mail.util.MailSSLSocketFactory.(MailSSLSocketFactory.java:70)
at com.sun.mail.util.SocketFetcher.startTLS(SocketFetcher.java:509)
... 22 common frames

If the socketfactory config is exposed we can inject our own class and configure the trustmanager.

I haven't tested it, but I think you can already do that, by manually setting the sslsocketfactory property on the Session object (but it won't work in combination with authenticated proxy):

Session session = mailer.getSession();
session.getProperties().put("mail.smtp.ssl.socketFactory", yourSSLSocketFactory);

Here are some more details you could look into: https://stackoverflow.com/a/34773907/441662

In fact I came across some old unused code in the library specifically for configuring such a ConnectionFactory, but it is untested (see this gist).

KeyStoreInfo optionalKeyStoreInfo = new KeyStoreInfo(keyStorePath, password, type); // without type param defaults to "JKS"
KeyStoreInfo trustKeyStoreInfo = new KeyStoreInfo(keyStorePath, password, type);
SSLSocketFactory yourSSLSocketFactory = new SSLConfiguration(optionalKeyStoreInfo, trustKeyStoreInfo).getSSLSocketFactory();

Session session = mailer.getSession();
session.getProperties().put("mail.smtp.ssl.socketFactory", yourSSLSocketFactory);

If you could please check if this works for you, then I know what to do to integrate this into the library behind a better API.

Other than that, do you have suggestions how you would like the API design to be regarding providing a custom SSLSocketFactory or a class (for mail.smtp.ssl.socketFactory.class)?

Setting the property on the mailer session worked for us. Thank you very much, you saved us a lot of effort.

I think a method on the mailer builder would be an appropriate api.

Setting the property on the mailer session worked for us. Thank you very much, you saved us a lot of effort.

I think a method on the mailer builder would be an appropriate api.

So did you do this with the code I gave you? Did you use one or two key stores?

I did this:
Properties mailerProps = mailer.getSession().getProperties(); mailerProps.put("mail.smtp.ssl.socketFactory.class", FIPSCompatibleMailSSLSocketFactory.class.getCanonicalName());

Would you like me to test configuring keystores?

I've added the following methods:

mailerBuilder
   .withCustomSSLFactoryClass(theClassName)
   .withCustomSSLFactoryInstance(theInstance) // takes precedence
   .buildMailer();

Including property support:

simplejavamail.custom.sslfactory.class=

Released in 6.3.0.