0N/A Notes for use of SSL with JavaMail
0N/A ----------------------------------
0N/A
0N/AJavaMail now supports accessing mail servers over connections secured
73N/Ausing SSL or TLS. To simplify such access, there are two alternative
73N/Aapproaches to enable use of SSL.
73N/A
73N/AFirst, and perhaps the simplest, is to set a property to enable use
73N/Aof SSL. For example, to enable use of SSL for SMTP connections, set
73N/Athe property "mail.smtp.ssl.enable" to "true".
73N/A
73N/AAlternatively, you can configure JavaMail to use one of the SSL-enabled
73N/Aprotocol names. In addition to the non-SSL JavaMail protocols "imap",
0N/A"pop3", and "smtp", the protocols "imaps", "pop3s", and "smtps" can
0N/Abe used to connect to the corresponding services using an SSL
0N/Aconnection.
0N/A
0N/AIn addition, the "imap" and "smtp" protocols support use of the
0N/ASTARTTLS command (see RFC 2487 and RFC 3501) to switch the connection
0N/Ato be secured by TLS.
0N/A
0N/AUse of the STARTTLS command is preferred in cases where the server
73N/Asupports both SSL and non-SSL connections.
0N/A
0N/AThis SSL/TLS support in JavaMail works only when JavaMail is used on
0N/Aa version of J2SE that includes SSL support. We have tested this
73N/Asupport on J2SE 1.4 and newer, which include SSL support. The
0N/ASSL support is provided by the JSSE package, which is also available
0N/Afor earlier versions of J2SE. We have not tested such configurations.
0N/A
0N/A-- STARTTLS support
0N/A
0N/AThe STARTTLS support is available in the standard "imap" and "smtp"
0N/Aprotocols, but must be enabled by setting the appropriate property,
0N/Amail.imap.starttls.enable or mail.smtp.starttls.enable, to "true".
0N/AWhen set, if the server supports the STARTTLS command, it will be
0N/Aused after making the connection and before sending any login
0N/Ainformation.
0N/A
0N/A
0N/A-- Secure protocols
0N/A
0N/AWhen using the new protocol names, configuration properties must also use
0N/Athese protocol names. For instance, set the property "mail.smtps.host"
0N/Ato specify the host name of the machine to connect to when using the
73N/A"smtps" protocol for SMTP over SSL. Similarly, to set the IMAP protocol
73N/Atimeout when using the "imaps" protocol for IMAP over SSL, set the property
0N/A"mail.imaps.timeout". See the package documentation for the different
0N/Aprotocol packages for the list of available properties, which are
0N/Aalways set using property names of the form mail.<protocol>.<property>.
0N/A
0N/AThe Transport.send method will use the default transport protocol,
73N/Awhich remains "smtp". To enable SMTP connections over SSL, set the
73N/A"mail.smtp.ssl.enable" property to "true". This is usually the easiest
73N/Aapproach.
73N/A
73N/AAlternatively, to change the default transport protocol
0N/Areturned by the Session.getTransport() method to SMTP over SSL, set
0N/Athe property "mail.transport.protocol" to "smtps". To change the
0N/Atransport used for internet addresses (as returned by the
0N/ASession.getTransport(Address) method, and used by the Transport.send
0N/Amethod), use
0N/A
0N/A session.setProtocolForAddress("rfc822", "smtps");
0N/A
0N/A
0N/A-- Trusted Certificates
0N/A
0N/ATo establish an SSL/TLS connection, the JavaMail client must be able
0N/Ato verify that the security certificate presented by the server
0N/Ait is connecting to is "trusted" by the client. Trusted certificates
0N/Aare maintained in a Java keystore file on the client. The J2SE
0N/ASDK "keytool" command is used to maintain the keystore file.
0N/A
0N/AThere are two common approaches for verifying server certificates.
0N/AThe first approach is probably most common for servers accessible to
0N/Apartners outside a company. The second approach is probably most
0N/Acommon for servers used within a company.
0N/A
0N/A1. Server certificates may be signed be a well known public
0N/A Certificate Authority. The default Java keystore file contains
0N/A the public keys of well known Certificate Authorities and can
0N/A verify the server's certificate by following the chain of
0N/A certificates signing the server's certificate back to one of
0N/A these well known CA certificates.
0N/A
0N/A In this case the client doesn't need to manage certificates
0N/A explicitly but can just use the default keystore file.
0N/A
0N/A2. Server certificates may be "self-signed". In this case there is
0N/A no chain of signatures to use in verifying the server's certificate.
0N/A Instead, the client will need the server's certificate in the
0N/A client's keystore file. The server's certificate is imported into
0N/A the keystore file once, using the keytool command, and after that
0N/A is used to verify connections to the server. A single keystore file
0N/A may contain certificates of many servers.
0N/A
0N/A In this case the client will need to set the appropriate System
0N/A properties to point to the client's keystore file containing the
0N/A trusted certificate. These properties can be set when invoking
0N/A the "java" command, or can be set programmatically. For example,
0N/A
0N/A java -Djavax.net.ssl.trustStore=$HOME/.keystore ...
0N/A
0N/A See the JSSE Reference Guide for details:
324N/A http://download.oracle.com/javase/6/docs/technotes/guides/security/jsse/JSSERefGuide.html#CustomizingStores
0N/A
93N/A
93N/A-- Server Identity Check
93N/A
93N/ARFC 2595 specifies addition checks that must be performed on the
93N/Aserver's certificate to ensure that the server you connected to is
93N/Athe server you intended to connect to. This reduces the risk of
93N/A"man in the middle" attacks. For compatibility with earlier releases
93N/Aof JavaMail, these additional checks are disabled by default. We
93N/Astrongly recommend that you enable these checks when using SSL. To
93N/Aenable these checks, set the "mail.<protocol>.ssl.checkserveridentity"
93N/Aproperty to "true".
93N/A
93N/A
93N/A-- Socket Factories
93N/A
93N/AIn earlier releases it was necessary to explicitly set a socket
93N/Afactory property to enable use of SSL. In almost all cases, this
93N/Ais no longer necessary. SSL support is built in. However, there
93N/Ais one case where a special socket factory may be needed.
93N/A
93N/AJavaMail now includes a special SSL socket factory that can simplify
93N/Adealing with servers with self-signed certificates. While the
93N/Arecommended approach is to include the certificate in your keystore
93N/Aas described above, the following approach may be simpler in some cases.
93N/A
93N/AThe class com.sun.mail.util.MailSSLSocketFactory can be used as a
93N/Asimple socket factory that allows trusting all hosts or a specific set
93N/Aof hosts. For example:
93N/A
93N/A MailSSLSocketFactory sf = new MailSSLSocketFactory();
93N/A sf.setTrustAllHosts(true);
93N/A // or
93N/A // sf.setTrustedHosts(new String[] { "my-server" });
93N/A props.put("mail.smtp.ssl.enable", "true");
93N/A // also use following for additional safety
93N/A //props.put("mail.smtp.ssl.checkserveridentity", "true");
93N/A props.put("mail.smtp.ssl.socketFactory", sf);
93N/A
93N/AUse of MailSSLSocketFactory avoids the need to add the certificate to
93N/Ayour keystore as described above, or configure your own TrustManager
93N/Aas described below.
0N/A
0N/A
0N/A-- Debugging
0N/A
0N/ADebugging problems with certificates and keystores can be difficult.
0N/AThe JSSE Reference Guide contains information on debugging utilities
0N/Athat can help. See:
324N/Ahttp://download.oracle.com/javase/6/docs/technotes/guides/security/jsse/JSSERefGuide.html#Debug
0N/A
0N/AThere are some debugging options in the JDK that can help, depending
0N/Aon the sorts of problems you're having. Setting the following system
0N/Aproperties will produce additional debugging output:
0N/A
0N/A java.security.debug=certpath
0N/A javax.net.debug=trustmanager
0N/A
0N/ASet these on the command line when you run your program using, for example:
0N/A
0N/A java -Djava.security.debug=certpath -Djavax.net.debug=trustmanager ...
0N/A
0N/A
0N/A-- keytool Usage
0N/A
0N/AGiven a certificate for the server as used in case #2 above, you can
0N/Aimport this certificate into your Java keystore file using a command
0N/Asuch as:
0N/A
0N/A keytool -import -alias imap-server -file imap.cer
0N/A
0N/AThe keytool command can also be used to generate a self-signed certificate
0N/Athat can be used by your mail server, if you're setting up your own server.
0N/AOther utilities, such as those included with the OpenSSL package, can also
0N/Abe used to generate such certificates, and they can be imported into the
0N/AJava keystore using keytool.
0N/A
0N/AFor more information on using the keytool command, see the keytool
0N/Areference pages at:
324N/Ahttp://download.oracle.com/javase/6/docs/technotes/guides/security/index.html
0N/A
0N/A
0N/A-- Configuring Your Own Trust Manager
0N/A
0N/AWhen using SSL/TLS, it's important to ensure that the server you connect
0N/Ato is actually the server you expected to connect to, to prevent "man in
0N/Athe middle" attacks on your communication. The recommended technique is
0N/Ato configure the Java keystore using one of the methods described above.
0N/AIf, for some reason, that approach is not workable, it's also possible
0N/Ato configure the SSL/TLS implementation to use your own TrustManager
0N/Aclass to evaluate whether to trust the server you've connected to.
0N/A
0N/AThe following "dummy" classes illustrate the framework necessary to create
0N/Ayour own TrustManager implementation.
0N/A
0N/AFirst, a replacement for the standard SSLSocketFactory is needed, to allow
0N/Ayou to specify which TrustManager to use:
0N/A
0N/A==> DummySSLSocketFactory.java <==
0N/A
0N/Aimport java.io.IOException;
0N/Aimport java.net.InetAddress;
0N/Aimport java.net.Socket;
0N/A
0N/Aimport javax.net.SocketFactory;
0N/Aimport javax.net.ssl.*;
0N/A
0N/A
0N/A/**
0N/A * DummySSLSocketFactory
0N/A */
0N/Apublic class DummySSLSocketFactory extends SSLSocketFactory {
0N/A private SSLSocketFactory factory;
0N/A
0N/A public DummySSLSocketFactory() {
0N/A try {
0N/A SSLContext sslcontext = SSLContext.getInstance("TLS");
0N/A sslcontext.init(null,
0N/A new TrustManager[] { new DummyTrustManager()},
0N/A null);
0N/A factory = (SSLSocketFactory)sslcontext.getSocketFactory();
0N/A } catch(Exception ex) {
0N/A // ignore
0N/A }
0N/A }
0N/A
0N/A public static SocketFactory getDefault() {
0N/A return new DummySSLSocketFactory();
0N/A }
0N/A
0N/A public Socket createSocket() throws IOException {
0N/A return factory.createSocket();
0N/A }
0N/A
0N/A public Socket createSocket(Socket socket, String s, int i, boolean flag)
0N/A throws IOException {
0N/A return factory.createSocket(socket, s, i, flag);
0N/A }
0N/A
0N/A public Socket createSocket(InetAddress inaddr, int i,
0N/A InetAddress inaddr1, int j) throws IOException {
0N/A return factory.createSocket(inaddr, i, inaddr1, j);
0N/A }
0N/A
0N/A public Socket createSocket(InetAddress inaddr, int i)
0N/A throws IOException {
0N/A return factory.createSocket(inaddr, i);
0N/A }
0N/A
0N/A public Socket createSocket(String s, int i, InetAddress inaddr, int j)
0N/A throws IOException {
0N/A return factory.createSocket(s, i, inaddr, j);
0N/A }
0N/A
0N/A public Socket createSocket(String s, int i) throws IOException {
0N/A return factory.createSocket(s, i);
0N/A }
0N/A
0N/A public String[] getDefaultCipherSuites() {
0N/A return factory.getDefaultCipherSuites();
0N/A }
0N/A
0N/A public String[] getSupportedCipherSuites() {
0N/A return factory.getSupportedCipherSuites();
0N/A }
0N/A}
0N/A
0N/A
0N/ANext you need the actual implementation of the TrustManager. This dummy
0N/Atrust manager trusts anything. THIS IS NOT SECURE!!!
0N/A
0N/A==> DummyTrustManager.java <==
0N/A
0N/Aimport javax.net.ssl.X509TrustManager;
0N/Aimport java.security.cert.X509Certificate;
0N/A
0N/A
0N/A/**
0N/A * DummyTrustManager - NOT SECURE
0N/A */
0N/Apublic class DummyTrustManager implements X509TrustManager {
0N/A
0N/A public void checkClientTrusted(X509Certificate[] cert, String authType) {
0N/A // everything is trusted
0N/A }
0N/A
0N/A public void checkServerTrusted(X509Certificate[] cert, String authType) {
0N/A // everything is trusted
0N/A }
0N/A
0N/A public X509Certificate[] getAcceptedIssuers() {
0N/A return new X509Certificate[0];
0N/A }
0N/A}
0N/A
73N/AFinally, you need to configure JavaMail to use your SSLSocketFactory.
73N/ASet the appropriate protocol-specific property, e.g.,
0N/A
73N/A props.setProperty("mail.imap.ssl.enable", "true");
73N/A props.setProperty("mail.imap.ssl.socketFactory.class",
0N/A "DummySSLSocketFactory");
73N/A props.setProperty("mail.imap.ssl.socketFactory.fallback", "false");
0N/A Session session = Session.getInstance(props, null);
0N/A
0N/ASimilar properties would need to be set to use other protocols.