Skip to content

Commit

Permalink
Merge branch 'fix_tls' of https://github.com/cbarcenas/simple-java-mail
Browse files Browse the repository at this point in the history
… into cbarcenas-fix_tls
  • Loading branch information
bbottema committed Nov 5, 2017
2 parents 4117a52 + 633aa4d commit 958c187
Show file tree
Hide file tree
Showing 2 changed files with 66 additions and 12 deletions.
Expand Up @@ -10,23 +10,48 @@
* <code>TransportStrategy</code> implementation.
*
* @author Benny Bottema
* @author Christian Barcenas
*/
public enum TransportStrategy {

/**
* Simplest possible form: only vanilla ".smtp." property names and no extra properties. Additionally the transport protocol is explicitly set to
* smtp.
* Vanilla SMTP with an insecure STARTTLS upgrade (if supported).
* <p>
* This {@code TransportStrategy} falls back to plaintext when a mail server does not indicate support for
* STARTTLS. Additionally, even if a TLS session is negotiated, <strong>server certificates are not validated in
* any way</strong>.
* <p>
* This {@code TransportStrategy} only offers protection against passive network eavesdroppers when the mail server
* indicates support for STARTTLS. Active network attackers can trivially bypass the encryption 1) by tampering with
* the STARTTLS indicator, 2) by presenting a self-signed certificate, 3) by presenting a certificate issued by an
* untrusted certificate authority; or 4) by presenting a certificate that was issued by a valid certificate
* authority to a domain other than the mail server's.
* <p>
* For proper mail transport encryption, see {@link TransportStrategy#SMTP_SSL} or
* {@link TransportStrategy#SMTP_TLS}.
* <p>
* Implementation notes:
* <ul>
* <li>The transport protocol is explicitly set to {@code smtp}.</li>
* <li>Only {@code mail.smtp} properties are set.</li>
* <li>STARTTLS is enabled by setting {@code mail.smtp.starttls.enable} to {@code true}.</li>
* <li>STARTTLS plaintext fallback is enabled by setting {@code mail.smtp.starttls.required} to {@code false}.</li>
* <li>Certificate issuer checks are disabled by setting {@code mail.smtp.ssl.trust} to {@code "*"}.</li>
* <li>Certificate identity checks are disabled by setting {@code mail.smtp.ssl.checkserveridentity} to {@code false}.</li>
* </ul>
*/
SMTP_PLAIN {
/**
* Here protocol "mail.transport.protocol" is set to "smtp".
*
* @see TransportStrategy#SMTP_PLAIN
*/
@Override
public Properties generateProperties() {
final Properties props = super.generateProperties();
props.put("mail.transport.protocol", "smtp");
props.put("mail.smtp.starttls.enable", true);
props.put("mail.smtp.starttls.required", false);
props.put("mail.smtp.ssl.trust", "*");
props.put("mail.smtp.ssl.checkserveridentity", false);
return props;
}

Expand Down Expand Up @@ -119,9 +144,18 @@ public String propertyNameSSLTrust() {
}
},
/**
* SMTPS / SSL transport strategy, that returns the ".smtps." variation of the SMTP_PLAIN version. Additionally the transport protocol is
* explicitly set to smtps. Finally, he property "mail.smtps.quitwait" is set to false, to get rid of a strange SSL exception:<br>
* SMTP entirely encapsulated by TLS. Commonly known as SMTPS.
* <p>
* Strict validation of server certificates is enabled. Server certificates must be issued 1) by a certificate
* authority in the system trust store; and 2) to a subject matching the identity of the remote SMTP server.
* <p>
* Implementation notes:
* <ul>
* <li>The transport protocol is explicitly set to {@code smtps}.</li>
* <li>Only {@code mail.smtps} properties are set.</li>
* <li>Certificate identity checks are enabled by setting {@code mail.smtp.ssl.checkserveridentity} to {@code true}.</li>
* <li>
* {@code mail.smtps.quitwait} is set to {@code false} to get rid of a strange SSLException:
* <pre>
* javax.mail.MessagingException: Exception reading response;
* nested exception is:
Expand All @@ -132,17 +166,18 @@ public String propertyNameSSLTrust() {
* <blockquote>The mail is sent but the exception is unwanted. The property <em>quitwait</em> means If set to false, the QUIT command is sent and
* the connection is immediately closed. If set to true (the default), causes the transport to wait for the response to the QUIT
* command</blockquote><br> <strong>- <a href="http://www.rgagnon.com/javadetails/java-0570.html">source</a></strong>
* </li>
* </ul>
*/
SMTP_SSL {
/**
* Here protocol "mail.transport.protocol" is set to "smtps" and the quitwait property "mail.smtps.quitwait" is set to "false".
*
* @see TransportStrategy#SMTP_SSL
*/
@Override
public Properties generateProperties() {
final Properties properties = super.generateProperties();
properties.put("mail.transport.protocol", "smtps");
properties.put("mail.smtps.ssl.checkserveridentity", "true");
properties.put("mail.smtps.quitwait", "false");
return properties;
}
Expand Down Expand Up @@ -236,22 +271,33 @@ public String propertyNameSSLTrust() {
}
},
/**
* Plaintext SMTP with a mandatory, authenticated STARTTLS upgrade.
* <p>
* <strong>NOTE: this code is in untested beta state</strong>
* <p>
* Uses standard ".smtp." property names (like {@link TransportStrategy#SMTP_PLAIN}). Additionally the transport protocol is explicitly set to
* smtp. Finally, the property "mail.smtp.starttls.enable" is being set to true.
* Strict validation of server certificates is enabled. Server certificates must be issued 1) by a certificate
* authority in the system trust store; and 2) to a subject matching the identity of the remote SMTP server.
* <p>
* Implementation notes:
* <ul>
* <li>The transport protocol is explicitly set to {@code smtp}.</li>
* <li>Only {@code mail.smtp} properties are set.</li>
* <li>STARTTLS is enabled by setting {@code mail.smtp.starttls.enable} to {@code true}.</li>
* <li>STARTTLS plaintext fallback is disabled by setting {@code mail.smtp.starttls.required} to {@code true}.</li>
* <li>Certificate identity checks are enabled by setting {@code mail.smtp.ssl.checkserveridentity} to {@code true}.</li>
* </ul>
*/
SMTP_TLS {
/**
* Here protocol "mail.transport.protocol" is set to "smtp" and the tls property "mail.smtp.starttls.enable" is set to "true".
*
* @see TransportStrategy#SMTP_TLS
*/
@Override
public Properties generateProperties() {
final Properties props = super.generateProperties();
props.put("mail.transport.protocol", "smtp");
props.put("mail.smtp.starttls.enable", "true");
props.put("mail.smtp.starttls.required", "true");
props.put("mail.smtp.ssl.checkserveridentity", "true");
return props;
}

Expand Down
8 changes: 8 additions & 0 deletions src/test/java/org/simplejavamail/mailer/MailerTest.java
Expand Up @@ -93,6 +93,8 @@ public void createMailSession_AnonymousProxyConstructor_WithoutConfig()
assertThat(session.getProperty("mail.smtp.port")).isEqualTo("25");
assertThat(session.getProperty("mail.transport.protocol")).isEqualTo("smtp");
assertThat(session.getProperty("mail.smtp.starttls.enable")).isEqualTo("true");
assertThat(session.getProperty("mail.smtp.starttls.required")).isEqualTo("true");
assertThat(session.getProperty("mail.smtp.ssl.checkserveridentity")).isEqualTo("true");
assertThat(session.getProperty("mail.smtp.username")).isEqualTo("username smtp");
assertThat(session.getProperty("mail.smtp.auth")).isEqualTo("true");
assertThat(session.getProperty("mail.smtp.socks.host")).isEqualTo("proxy host");
Expand All @@ -115,6 +117,8 @@ public void createMailSession_MaximumConstructor_WithoutConfig()
assertThat(session.getProperty("mail.smtp.port")).isEqualTo("25");
assertThat(session.getProperty("mail.transport.protocol")).isEqualTo("smtp");
assertThat(session.getProperty("mail.smtp.starttls.enable")).isEqualTo("true");
assertThat(session.getProperty("mail.smtp.starttls.required")).isEqualTo("true");
assertThat(session.getProperty("mail.smtp.ssl.checkserveridentity")).isEqualTo("true");
assertThat(session.getProperty("mail.smtp.username")).isEqualTo("username smtp");
assertThat(session.getProperty("mail.smtp.auth")).isEqualTo("true");
// the following two are because authentication is needed, otherwise proxy would be straightworward
Expand All @@ -134,6 +138,8 @@ public void createMailSession_MinimalConstructor_WithConfig() {
assertThat(session.getProperty("mail.smtp.port")).isEqualTo("25");
assertThat(session.getProperty("mail.transport.protocol")).isEqualTo("smtp");
assertThat(session.getProperty("mail.smtp.starttls.enable")).isEqualTo("true");
assertThat(session.getProperty("mail.smtp.starttls.required")).isEqualTo("true");
assertThat(session.getProperty("mail.smtp.ssl.checkserveridentity")).isEqualTo("true");
assertThat(session.getProperty("mail.smtp.username")).isEqualTo("username smtp");
assertThat(session.getProperty("mail.smtp.auth")).isEqualTo("true");
// the following two are because authentication is needed, otherwise proxy would be straightworward
Expand All @@ -153,6 +159,8 @@ public void createMailSession_MaximumConstructor_WithConfig()
assertThat(session.getProperty("mail.smtp.port")).isEqualTo("25");
assertThat(session.getProperty("mail.transport.protocol")).isEqualTo("smtp");
assertThat(session.getProperty("mail.smtp.starttls.enable")).isEqualTo("true");
assertThat(session.getProperty("mail.smtp.starttls.required")).isEqualTo("true");
assertThat(session.getProperty("mail.smtp.ssl.checkserveridentity")).isEqualTo("true");
assertThat(session.getProperty("mail.smtp.username")).isEqualTo("overridden username smtp");
assertThat(session.getProperty("mail.smtp.auth")).isEqualTo("true");
// the following two are because authentication is needed, otherwise proxy would be straightworward
Expand Down

1 comment on commit 958c187

@bbottema
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Implements #105

Please sign in to comment.