JavaMail now supports accessing mail servers over connections secured using SSL or TLS. To simplify such access, there are two alternative approaches to enable use of SSL.
First, and perhaps the simplest, is to set a property to enable use of SSL. For example, to enable use of SSL for SMTP connections, set the property "mail.smtp.ssl.enable" to "true".
Alternatively, you can configure JavaMail to use one of the SSL-enabled protocol names. In addition to the non-SSL JavaMail protocols "imap", "pop3", and "smtp", the protocols "imaps", "pop3s", and "smtps" can be used to connect to the corresponding services using an SSL connection.
In addition, the "imap" and "smtp" protocols support use of the STARTTLS command (see RFC 2487 and RFC 3501) to switch the connection to be secured by TLS.
Use of the STARTTLS command is preferred in cases where the server supports both SSL and non-SSL connections.
This SSL/TLS support in JavaMail works only when JavaMail is used on a version of J2SE that includes SSL support. We have tested this support on J2SE 1.4 and newer, which include SSL support. The SSL support is provided by the JSSE package, which is also available for earlier versions of J2SE. We have not tested such configurations.
The STARTTLS support is available in the standard "imap" and "smtp" protocols, but must be enabled by setting the appropriate property, mail.imap.starttls.enable or mail.smtp.starttls.enable, to "true". When set, if the server supports the STARTTLS command, it will be used after making the connection and before sending any login information.
When using the new protocol names, configuration properties must also use these protocol names. For instance, set the property "mail.smtps.host" to specify the host name of the machine to connect to when using the "smtps" protocol for SMTP over SSL. Similarly, to set the IMAP protocol timeout when using the "imaps" protocol for IMAP over SSL, set the property "mail.imaps.timeout". See the package documentation for the different protocol packages for the list of available properties, which are always set using property names of the form mail.<protocol>.<property>.
The Transport.send method will use the default transport protocol, which remains "smtp". To enable SMTP connections over SSL, set the "mail.smtp.ssl.enable" property to "true". This is usually the easiest approach.
Alternatively, to change the default transport protocol
returned by the Session.getTransport() method to SMTP over SSL, set
the property "mail.transport.protocol" to "smtps". To change the
transport used for internet addresses (as returned by the
Session.getTransport(Address) method, and used by the Transport.send
method), use
session.setProtocolForAddress("rfc822", "smtps");
To establish an SSL/TLS connection, the JavaMail client must be able to verify that the security certificate presented by the server it is connecting to is "trusted" by the client. Trusted certificates are maintained in a Java keystore file on the client. The J2SE SDK "keytool" command is used to maintain the keystore file.
There are two common approaches for verifying server certificates. The first approach is probably most common for servers accessible to partners outside a company. The second approach is probably most common for servers used within a company.
See the JSSE Reference Guide for details:https://docs.oracle.com/javase/6/docs/technotes/guides/security/jsse/JSSERefGuide.html#CustomizingStores
RFC 2595 specifies addition checks that must be performed on the server's certificate to ensure that the server you connected to is the server you intended to connect to. This reduces the risk of "man in the middle" attacks. For compatibility with earlier releases of JavaMail, these additional checks are disabled by default. We strongly recommend that you enable these checks when using SSL. To enable these checks, set the "mail.<protocol>.ssl.checkserveridentity" property to "true".
In earlier releases it was necessary to explicitly set a socket factory property to enable use of SSL. In almost all cases, this is no longer necessary. SSL support is built in. However, there is one case where a special socket factory may be needed.
JavaMail now includes a special SSL socket factory that can simplify dealing with servers with self-signed certificates. While the recommended approach is to include the certificate in your keystore as described above, the following approach may be simpler in some cases.
The class com.sun.mail.util.MailSSLSocketFactory can be used as a simple socket factory that allows trusting all hosts or a specific set of hosts. For example:
MailSSLSocketFactory sf = new MailSSLSocketFactory();
sf.setTrustAllHosts(true);
// or
// sf.setTrustedHosts(new String[] { "my-server" });
props.put("mail.smtp.ssl.enable", "true");
// also use following for additional safety
//props.put("mail.smtp.ssl.checkserveridentity", "true");
props.put("mail.smtp.ssl.socketFactory", sf);
Use of MailSSLSocketFactory avoids the need to add the certificate to your keystore as described above, or configure your own TrustManager as described below.
Debugging problems with certificates and keystores can be difficult. The JSSE Reference Guide contains information on debugging utilities that can help. See:https://docs.oracle.com/javase/6/docs/technotes/guides/security/jsse/JSSERefGuide.html#Debug
There are some debugging options in the JDK that can help, depending on the sorts of problems you're having. Setting the following system properties will produce additional debugging output:
java.security.debug=certpath
javax.net.debug=trustmanager
Set these on the command line when you run your program using, for example:
java -Djava.security.debug=certpath -Djavax.net.debug=trustmanager ...
Given a certificate for the server as used in case #2 above, you can
import this certificate into your Java keystore file using a command
such as:
keytool -import -alias imap-server -file imap.cer
The keytool command can also be used to generate a self-signed certificate that can be used by your mail server, if you're setting up your own server. Other utilities, such as those included with the OpenSSL package, can also be used to generate such certificates, and they can be imported into the Java keystore using keytool.
For more information on using the keytool command, see the keytool reference pages at:https://docs.oracle.com/javase/6/docs/technotes/guides/security/index.html
When using SSL/TLS, it's important to ensure that the server you connect to is actually the server you expected to connect to, to prevent "man in the middle" attacks on your communication. The recommended technique is to configure the Java keystore using one of the methods described above. If, for some reason, that approach is not workable, it's also possible to configure the SSL/TLS implementation to use your own TrustManager class to evaluate whether to trust the server you've connected to.
The following "dummy" classes illustrate the framework necessary to create your own TrustManager implementation.
First, a replacement for the standard SSLSocketFactory is needed, to allow you to specify which TrustManager to use:
==> DummySSLSocketFactory.java <==
import java.io.IOException;
import java.net.InetAddress;
import java.net.Socket;
import javax.net.SocketFactory;
import javax.net.ssl.*;
/**
* DummySSLSocketFactory
*/
public class DummySSLSocketFactory extends SSLSocketFactory {
private SSLSocketFactory factory;
public DummySSLSocketFactory() {
try {
SSLContext sslcontext = SSLContext.getInstance("TLS");
sslcontext.init(null,
new TrustManager[] { new DummyTrustManager()},
null);
factory = (SSLSocketFactory)sslcontext.getSocketFactory();
} catch(Exception ex) {
// ignore
}
}
public static SocketFactory getDefault() {
return new DummySSLSocketFactory();
}
public Socket createSocket() throws IOException {
return factory.createSocket();
}
public Socket createSocket(Socket socket, String s, int i, boolean flag)
throws IOException {
return factory.createSocket(socket, s, i, flag);
}
public Socket createSocket(InetAddress inaddr, int i,
InetAddress inaddr1, int j) throws IOException {
return factory.createSocket(inaddr, i, inaddr1, j);
}
public Socket createSocket(InetAddress inaddr, int i)
throws IOException {
return factory.createSocket(inaddr, i);
}
public Socket createSocket(String s, int i, InetAddress inaddr, int j)
throws IOException {
return factory.createSocket(s, i, inaddr, j);
}
public Socket createSocket(String s, int i) throws IOException {
return factory.createSocket(s, i);
}
public String[] getDefaultCipherSuites() {
return factory.getDefaultCipherSuites();
}
public String[] getSupportedCipherSuites() {
return factory.getSupportedCipherSuites();
}
}
Next you need the actual implementation of the TrustManager. This dummy trust manager trusts anything. THIS IS NOT SECURE!!!
==> DummyTrustManager.java <==
import javax.net.ssl.X509TrustManager;
import java.security.cert.X509Certificate;
/**
* DummyTrustManager - NOT SECURE
*/
public class DummyTrustManager implements X509TrustManager {
public void checkClientTrusted(X509Certificate[] cert, String authType) {
// everything is trusted
}
public void checkServerTrusted(X509Certificate[] cert, String authType) {
// everything is trusted
}
public X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[0];
}
}
Finally, you need to configure JavaMail to use your SSLSocketFactory. Set the appropriate protocol-specific property, e.g.,
props.setProperty("mail.imap.ssl.enable", "true");
props.setProperty("mail.imap.ssl.socketFactory.class",
"DummySSLSocketFactory");
props.setProperty("mail.imap.ssl.socketFactory.fallback", "false");
Session session = Session.getInstance(props, null);
Similar properties would need to be set to use other protocols.