Skip to content

Commit 1b25fbb

Browse files
committed
#320: added byDefault S/MIME signing on the Mailer level. Also, the smime properties are not set on Email anymore but on Mailer. S/MIME signing can be overridden on a per/Email basis. Consolidated all such properties (email validation criteria, S/MIME default signing, DKIM in the future as well) in new mailer level config object called EmailGovernance.
1 parent e93123f commit 1b25fbb

File tree

20 files changed

+422
-84
lines changed

20 files changed

+422
-84
lines changed

modules/core-module/src/main/java/org/simplejavamail/api/email/EmailPopulatingBuilder.java

+5-3
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
package org.simplejavamail.api.email;
22

3+
import org.jetbrains.annotations.NotNull;
4+
import org.jetbrains.annotations.Nullable;
35
import org.simplejavamail.api.internal.clisupport.model.Cli;
46
import org.simplejavamail.api.internal.clisupport.model.CliBuilderApiType;
57
import org.simplejavamail.api.mailer.config.Pkcs12Config;
68

79
import javax.activation.DataSource;
8-
import org.jetbrains.annotations.NotNull;
9-
import org.jetbrains.annotations.Nullable;
1010
import javax.mail.Message;
1111
import javax.mail.internet.InternetAddress;
1212
import javax.mail.internet.MimeMessage;
@@ -1127,10 +1127,12 @@ public interface EmailPopulatingBuilder {
11271127
* Signs this email with an <a href="https://tools.ietf.org/html/rfc5751">S/MIME</a> signature, so the receiving client
11281128
* can verify whether the email content was tampered with.
11291129
* <p>
1130-
* <strong>Note:</strong> this only works in combination with the {@value org.simplejavamail.internal.modules.SMIMEModule#NAME}.
1130+
* <strong>Note:</strong> this only works in combination with the {@value org.simplejavamail.internal.modules.SMIMEModule#NAME}.<br>
1131+
* <strong>Note:</strong> You can also configure your <code>Mailer</code> instance do sign all emails by default (also has better performance).
11311132
*
11321133
* @see <a href="https://en.wikipedia.org/wiki/S/MIME">S/MIME on Wikipedia</a>
11331134
* @see <a href="https://www.globalsign.com/en/blog/what-is-s-mime/">Primer on S/MIME</a>
1135+
* @see org.simplejavamail.api.mailer.MailerGenericBuilder#signByDefaultWithSmime(Pkcs12Config)
11341136
*/
11351137
@Cli.ExcludeApi(reason = "delegated method contains CLI compatible arguments")
11361138
EmailPopulatingBuilder signWithSmime(@NotNull Pkcs12Config pkcs12Config);

modules/core-module/src/main/java/org/simplejavamail/api/mailer/Mailer.java

+5-7
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,19 @@
22

33
import org.hazlewood.connor.bottema.emailaddress.EmailAddressCriteria;
44
import org.hazlewood.connor.bottema.emailaddress.EmailAddressValidator;
5+
import org.jetbrains.annotations.NotNull;
6+
import org.jetbrains.annotations.Nullable;
57
import org.simplejavamail.MailException;
68
import org.simplejavamail.api.email.Email;
9+
import org.simplejavamail.api.mailer.config.EmailGovernance;
710
import org.simplejavamail.api.mailer.config.OperationalConfig;
811
import org.simplejavamail.api.mailer.config.ProxyConfig;
912
import org.simplejavamail.api.mailer.config.ServerConfig;
1013
import org.simplejavamail.api.mailer.config.TransportStrategy;
1114

12-
import org.jetbrains.annotations.NotNull;
13-
import org.jetbrains.annotations.Nullable;
1415
import javax.mail.Message;
1516
import javax.mail.Session;
1617
import javax.mail.Transport;
17-
import java.util.EnumSet;
1818
import java.util.concurrent.Future;
1919

2020
/**
@@ -148,10 +148,8 @@ public interface Mailer {
148148
OperationalConfig getOperationalConfig();
149149

150150
/**
151-
* @return The effective validation criteria used for email validation. Returns an empty set if no validation should be done.
152-
* @see MailerGenericBuilder#withEmailAddressCriteria(EnumSet)
153-
* @see EmailAddressCriteria
151+
* @return The effective governance applied to each email (default S/MIME signing, email addresscriteria for validation etc.).
154152
*/
155153
@NotNull
156-
EnumSet<EmailAddressCriteria> getEmailAddressCriteria();
154+
EmailGovernance getEmailGovernance();
157155
}

modules/core-module/src/main/java/org/simplejavamail/api/mailer/MailerGenericBuilder.java

+66-2
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,17 @@
11
package org.simplejavamail.api.mailer;
22

33
import org.hazlewood.connor.bottema.emailaddress.EmailAddressCriteria;
4+
import org.jetbrains.annotations.NotNull;
5+
import org.jetbrains.annotations.Nullable;
46
import org.simplejavamail.api.internal.clisupport.model.Cli;
57
import org.simplejavamail.api.internal.clisupport.model.CliBuilderApiType;
68
import org.simplejavamail.api.mailer.config.LoadBalancingStrategy;
9+
import org.simplejavamail.api.mailer.config.Pkcs12Config;
710
import org.simplejavamail.api.mailer.config.TransportStrategy;
811

9-
import org.jetbrains.annotations.NotNull;
10-
import org.jetbrains.annotations.Nullable;
1112
import javax.mail.Session;
13+
import java.io.File;
14+
import java.io.InputStream;
1215
import java.util.EnumSet;
1316
import java.util.List;
1417
import java.util.Map;
@@ -226,6 +229,53 @@ public interface MailerGenericBuilder<T extends MailerGenericBuilder<?>> {
226229
*/
227230
T withEmailAddressCriteria(@NotNull EnumSet<EmailAddressCriteria> emailAddressCriteria);
228231

232+
/**
233+
* Signs this <em>all emails by default</em> with an <a href="https://tools.ietf.org/html/rfc5751">S/MIME</a> signature, so the receiving client
234+
* can verify whether the email content was tampered with.
235+
* <p>
236+
* <strong>Note:</strong> this only works in combination with the {@value org.simplejavamail.internal.modules.SMIMEModule#NAME}.
237+
*
238+
* @see <a href="https://en.wikipedia.org/wiki/S/MIME">S/MIME on Wikipedia</a>
239+
* @see <a href="https://www.globalsign.com/en/blog/what-is-s-mime/">Primer on S/MIME</a>
240+
* @see org.simplejavamail.api.email.EmailPopulatingBuilder#signWithSmime(Pkcs12Config)
241+
* @see #clearSignByDefaultWithSmime()
242+
*/
243+
@Cli.ExcludeApi(reason = "delegated method contains CLI compatible arguments")
244+
T signByDefaultWithSmime(@NotNull Pkcs12Config pkcs12Config);
245+
246+
/**
247+
* Delegates to {@link #signByDefaultWithSmime(InputStream, String, String, String)}.
248+
* <p>
249+
* <strong>Note:</strong> this only works in combination with the {@value org.simplejavamail.internal.modules.SMIMEModule#NAME}.
250+
*
251+
* @param pkcs12StoreFile The key store file to use to find the indicated key
252+
* @param storePassword The store's password
253+
* @param keyAlias The name of the certificate in the key store to use
254+
* @param keyPassword The password of the certificate
255+
*/
256+
T signByDefaultWithSmime(@NotNull File pkcs12StoreFile, @NotNull String storePassword, @NotNull String keyAlias, @NotNull String keyPassword);
257+
258+
/**
259+
* Delegates to {@link #signByDefaultWithSmime(byte[], String, String, String)}.
260+
* <p>
261+
* <strong>Note:</strong> this only works in combination with the {@value org.simplejavamail.internal.modules.SMIMEModule#NAME}.
262+
*/
263+
@Cli.ExcludeApi(reason = "Is duplicate API from CLI point of view")
264+
T signByDefaultWithSmime(@NotNull InputStream pkcs12StoreStream, @NotNull String storePassword, @NotNull String keyAlias, @NotNull String keyPassword);
265+
266+
/**
267+
* Delegates to {@link #signByDefaultWithSmime(Pkcs12Config)}.
268+
* <p>
269+
* <strong>Note:</strong> this only works in combination with the {@value org.simplejavamail.internal.modules.SMIMEModule#NAME}.
270+
*
271+
* @param pkcs12StoreData The key store file to use to find the indicated key
272+
* @param storePassword The store's password
273+
* @param keyAlias The name of the certificate in the key store to use
274+
* @param keyPassword The password of the certificate
275+
*/
276+
@Cli.ExcludeApi(reason = "Is duplicate API from CLI point of view")
277+
T signByDefaultWithSmime(@NotNull byte[] pkcs12StoreData, @NotNull String storePassword, @NotNull String keyAlias, @NotNull String keyPassword);
278+
229279
/**
230280
* <strong>For advanced use cases.</strong>
231281
* <p>
@@ -580,6 +630,13 @@ public interface MailerGenericBuilder<T extends MailerGenericBuilder<?>> {
580630
*/
581631
T clearEmailAddressCriteria();
582632

633+
/**
634+
* Removes S/MIME signing, so emails won't be signed by default.
635+
*
636+
* @see #signByDefaultWithSmime(Pkcs12Config)
637+
*/
638+
T clearSignByDefaultWithSmime();
639+
583640
/**
584641
* Removes all trusted hosts from the list.
585642
*
@@ -651,6 +708,13 @@ public interface MailerGenericBuilder<T extends MailerGenericBuilder<?>> {
651708
@Nullable
652709
EnumSet<EmailAddressCriteria> getEmailAddressCriteria();
653710

711+
/**
712+
* @see #signByDefaultWithSmime(Pkcs12Config)
713+
* @see #signByDefaultWithSmime(InputStream, String, String, String)
714+
*/
715+
@Nullable
716+
Pkcs12Config getPkcs12ConfigForSmimeSigning();
717+
654718
/**
655719
* Returns the user set ExecutorService or else null as the default ExecutorService is not created until the {@link org.simplejavamail.api.mailer.config.OperationalConfig} is created for the
656720
* new {@link Mailer} instance.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
package org.simplejavamail.api.mailer.config;
2+
3+
import org.hazlewood.connor.bottema.emailaddress.EmailAddressCriteria;
4+
import org.jetbrains.annotations.NotNull;
5+
import org.jetbrains.annotations.Nullable;
6+
import org.simplejavamail.api.email.EmailPopulatingBuilder;
7+
import org.simplejavamail.api.mailer.MailerGenericBuilder;
8+
9+
import java.io.InputStream;
10+
import java.util.EnumSet;
11+
12+
/**
13+
* Governance for all emails being sent through the current {@link org.simplejavamail.api.mailer.Mailer} instance.
14+
* <p>
15+
* In simpeler terms: this class represents actions taken or configuration used by default for each individual email sent through the current mailer. For example, you might want to S/MIME sign all
16+
* emails by default. You can do it manually on each email of course, but then the keystore used for this not reused.
17+
*/
18+
public interface EmailGovernance {
19+
20+
/**
21+
* @return The effective validation criteria used for email validation. Returns an empty set if no validation should be done.
22+
* @see MailerGenericBuilder#withEmailAddressCriteria(EnumSet)
23+
* @see EmailAddressCriteria
24+
*/
25+
@NotNull
26+
EnumSet<EmailAddressCriteria> getEmailAddressCriteria();
27+
28+
/**
29+
* @see EmailPopulatingBuilder#signWithSmime(Pkcs12Config)
30+
* @see EmailPopulatingBuilder#signWithSmime(InputStream, String, String, String)
31+
* @see MailerGenericBuilder#signByDefaultWithSmime(Pkcs12Config)
32+
* @see MailerGenericBuilder#signByDefaultWithSmime(InputStream, String, String, String)
33+
*/
34+
@Nullable
35+
Pkcs12Config getPkcs12ConfigForSmimeSigning();
36+
}

modules/core-module/src/main/java/org/simplejavamail/internal/modules/SMIMEModule.java

+4-3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package org.simplejavamail.internal.modules;
22

3+
import org.jetbrains.annotations.NotNull;
4+
import org.jetbrains.annotations.Nullable;
35
import org.simplejavamail.api.email.AttachmentResource;
46
import org.simplejavamail.api.email.Email;
57
import org.simplejavamail.api.email.OriginalSmimeDetails;
@@ -9,8 +11,6 @@
911
import org.simplejavamail.api.internal.smimesupport.model.SmimeDetails;
1012
import org.simplejavamail.api.mailer.config.Pkcs12Config;
1113

12-
import org.jetbrains.annotations.NotNull;
13-
import org.jetbrains.annotations.Nullable;
1414
import javax.mail.Session;
1515
import javax.mail.internet.MimeMessage;
1616
import javax.mail.internet.MimePart;
@@ -70,7 +70,8 @@ public interface SMIMEModule {
7070
boolean verifyValidSignature(@NotNull MimeMessage mimeMessage, @NotNull OriginalSmimeDetails messageSmimeDetails);
7171

7272
@NotNull
73-
MimeMessage signAndOrEncryptEmail(@NotNull final Session session, @NotNull final MimeMessage messageToProtect, @NotNull final Email emailContainingSmimeDetails);
73+
MimeMessage signAndOrEncryptEmail(@NotNull final Session session, @NotNull final MimeMessage messageToProtect, @NotNull final Email emailContainingSmimeDetails,
74+
@Nullable final Pkcs12Config defaultSmimeSigningStore);
7475

7576
@NotNull
7677
MimeMessage signMessage(@Nullable Session session, @NotNull MimeMessage message, @NotNull Pkcs12Config pkcs12Config);

modules/simple-java-mail/src/main/java/org/simplejavamail/converter/EmailConverter.java

+20-2
Original file line numberDiff line numberDiff line change
@@ -428,11 +428,29 @@ public static MimeMessage emailToMimeMessage(@NotNull final Email email) {
428428
}
429429

430430
/**
431-
* Refer to {@link MimeMessageProducerHelper#produceMimeMessage(Email, Session)}.
431+
* Refer to {@link MimeMessageProducerHelper#produceMimeMessage(Email, Session, Pkcs12Config)}.
432+
*/
433+
public static MimeMessage emailToMimeMessage(@NotNull final Email email, @NotNull final Session session, @NotNull final Pkcs12Config defaultSmimeSigningStore) {
434+
try {
435+
return MimeMessageProducerHelper.produceMimeMessage(
436+
checkNonEmptyArgument(email, "email"),
437+
checkNonEmptyArgument(session, "session"),
438+
checkNonEmptyArgument(defaultSmimeSigningStore, "defaultSmimeSigningStore"));
439+
} catch (UnsupportedEncodingException | MessagingException e) {
440+
// this should never happen, so we don't acknowledge this exception (and simply bubble up)
441+
throw new IllegalStateException(e.getMessage(), e);
442+
}
443+
}
444+
445+
/**
446+
* Delegates to {@link MimeMessageProducerHelper#produceMimeMessage(Email, Session, Pkcs12Config)} with empty S/MIME signing store.
432447
*/
433448
public static MimeMessage emailToMimeMessage(@NotNull final Email email, @NotNull final Session session) {
434449
try {
435-
return MimeMessageProducerHelper.produceMimeMessage(checkNonEmptyArgument(email, "email"), checkNonEmptyArgument(session, "session"));
450+
return MimeMessageProducerHelper.produceMimeMessage(
451+
checkNonEmptyArgument(email, "email"),
452+
checkNonEmptyArgument(session, "session"),
453+
null);
436454
} catch (UnsupportedEncodingException | MessagingException e) {
437455
// this should never happen, so we don't acknowledge this exception (and simply bubble up)
438456
throw new IllegalStateException(e.getMessage(), e);

modules/simple-java-mail/src/main/java/org/simplejavamail/converter/internal/mimemessage/MimeMessageProducer.java

+5-4
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
package org.simplejavamail.converter.internal.mimemessage;
22

3+
import org.jetbrains.annotations.NotNull;
4+
import org.jetbrains.annotations.Nullable;
35
import org.simplejavamail.api.email.Email;
6+
import org.simplejavamail.api.mailer.config.Pkcs12Config;
47
import org.simplejavamail.internal.modules.ModuleLoader;
58

6-
import org.jetbrains.annotations.NotNull;
7-
89
import javax.mail.MessagingException;
910
import javax.mail.Session;
1011
import javax.mail.internet.MimeMessage;
@@ -37,7 +38,7 @@ public abstract class MimeMessageProducer {
3738
*/
3839
abstract boolean compatibleWithEmail(@NotNull Email email);
3940

40-
final MimeMessage populateMimeMessage(@NotNull final Email email, @NotNull Session session)
41+
final MimeMessage populateMimeMessage(@NotNull final Email email, @NotNull Session session, @Nullable final Pkcs12Config defaultSmimeSigningStore)
4142
throws MessagingException, UnsupportedEncodingException {
4243
checkArgumentNotEmpty(email, "email is missing");
4344
checkArgumentNotEmpty(session, "session is needed, it cannot be attached later");
@@ -80,7 +81,7 @@ public String toString() {
8081
3. DKIM signing
8182
*/
8283
if (ModuleLoader.smimeModuleAvailable()) {
83-
message = ModuleLoader.loadSmimeModule().signAndOrEncryptEmail(session, message, email);
84+
message = ModuleLoader.loadSmimeModule().signAndOrEncryptEmail(session, message, email, defaultSmimeSigningStore);
8485
}
8586

8687
if (!valueNullOrEmpty(email.getDkimSigningDomain())) {

modules/simple-java-mail/src/main/java/org/simplejavamail/converter/internal/mimemessage/MimeMessageProducerHelper.java

+5-3
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
package org.simplejavamail.converter.internal.mimemessage;
22

3+
import org.jetbrains.annotations.NotNull;
4+
import org.jetbrains.annotations.Nullable;
35
import org.simplejavamail.api.email.Email;
6+
import org.simplejavamail.api.mailer.config.Pkcs12Config;
47

5-
import org.jetbrains.annotations.NotNull;
68
import javax.mail.MessagingException;
79
import javax.mail.Session;
810
import javax.mail.internet.MimeMessage;
@@ -34,10 +36,10 @@ public final class MimeMessageProducerHelper {
3436
private MimeMessageProducerHelper() {
3537
}
3638

37-
public static MimeMessage produceMimeMessage(@NotNull Email email, @NotNull Session session) throws UnsupportedEncodingException, MessagingException {
39+
public static MimeMessage produceMimeMessage(@NotNull Email email, @NotNull Session session, @Nullable final Pkcs12Config defaultSmimeSigningStore) throws UnsupportedEncodingException, MessagingException {
3840
for (MimeMessageProducer mimeMessageProducer : mimeMessageProducers) {
3941
if (mimeMessageProducer.compatibleWithEmail(email)) {
40-
return mimeMessageProducer.populateMimeMessage(email, session);
42+
return mimeMessageProducer.populateMimeMessage(email, session, defaultSmimeSigningStore);
4143
}
4244
}
4345
throw new IllegalStateException("no compatible MimeMessageProducer found for email");

modules/simple-java-mail/src/main/java/org/simplejavamail/email/internal/EmailPopulatingBuilderImpl.java

-12
Original file line numberDiff line numberDiff line change
@@ -73,10 +73,6 @@
7373
import static org.simplejavamail.config.ConfigLoader.Property.EMBEDDEDIMAGES_DYNAMICRESOLUTION_OUTSIDE_BASE_DIR;
7474
import static org.simplejavamail.config.ConfigLoader.Property.EMBEDDEDIMAGES_DYNAMICRESOLUTION_OUTSIDE_BASE_URL;
7575
import static org.simplejavamail.config.ConfigLoader.Property.SMIME_ENCRYPTION_CERTIFICATE;
76-
import static org.simplejavamail.config.ConfigLoader.Property.SMIME_SIGNING_KEYSTORE;
77-
import static org.simplejavamail.config.ConfigLoader.Property.SMIME_SIGNING_KEYSTORE_PASSWORD;
78-
import static org.simplejavamail.config.ConfigLoader.Property.SMIME_SIGNING_KEY_ALIAS;
79-
import static org.simplejavamail.config.ConfigLoader.Property.SMIME_SIGNING_KEY_PASSWORD;
8076
import static org.simplejavamail.config.ConfigLoader.getBooleanProperty;
8177
import static org.simplejavamail.config.ConfigLoader.getProperty;
8278
import static org.simplejavamail.config.ConfigLoader.getStringProperty;
@@ -366,14 +362,6 @@ public class EmailPopulatingBuilderImpl implements InternalEmailPopulatingBuilde
366362
if (hasProperty(DEFAULT_SUBJECT)) {
367363
withSubject((String) getProperty(DEFAULT_SUBJECT));
368364
}
369-
if (hasProperty(SMIME_SIGNING_KEYSTORE)) {
370-
signWithSmime(Pkcs12Config.builder()
371-
.pkcs12Store(assumeNonNull(getStringProperty(SMIME_SIGNING_KEYSTORE)))
372-
.storePassword(checkNonEmptyArgument(getStringProperty(SMIME_SIGNING_KEYSTORE_PASSWORD), "Keystore password property"))
373-
.keyAlias(checkNonEmptyArgument(getStringProperty(SMIME_SIGNING_KEY_ALIAS), "Key alias property"))
374-
.keyPassword(checkNonEmptyArgument(getStringProperty(SMIME_SIGNING_KEY_PASSWORD), "Key password property"))
375-
.build());
376-
}
377365
if (hasProperty(SMIME_ENCRYPTION_CERTIFICATE)) {
378366
encryptWithSmime(assumeNonNull(getStringProperty(SMIME_ENCRYPTION_CERTIFICATE)));
379367
}

modules/simple-java-mail/src/main/java/org/simplejavamail/mailer/MailerHelper.java

+4-3
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import org.simplejavamail.api.email.AttachmentResource;
99
import org.simplejavamail.api.email.Email;
1010
import org.simplejavamail.api.email.Recipient;
11+
import org.simplejavamail.api.mailer.config.Pkcs12Config;
1112
import org.simplejavamail.internal.modules.ModuleLoader;
1213
import org.slf4j.Logger;
1314

@@ -139,11 +140,11 @@ public static MimeMessage signMessageWithDKIM(@NotNull final MimeMessage message
139140
/**
140141
* Depending on the Email configuration, signs and then encrypts message (both steps optional), using the S/MIME module.
141142
*
142-
* @see org.simplejavamail.internal.modules.SMIMEModule#signAndOrEncryptEmail(Session, MimeMessage, Email)
143+
* @see org.simplejavamail.internal.modules.SMIMEModule#signAndOrEncryptEmail(Session, MimeMessage, Email, Pkcs12Config)
143144
*/
144145
@SuppressWarnings("unused")
145-
public static MimeMessage signAndOrEncryptMessageWithSmime(@NotNull final Session session, @NotNull final MimeMessage messageToProtect, @NotNull final Email emailContainingSmimeDetails) {
146+
public static MimeMessage signAndOrEncryptMessageWithSmime(@NotNull final Session session, @NotNull final MimeMessage messageToProtect, @NotNull final Email emailContainingSmimeDetails, @Nullable final Pkcs12Config defaultSmimeSigningStore) {
146147
return ModuleLoader.loadSmimeModule()
147-
.signAndOrEncryptEmail(session, messageToProtect, emailContainingSmimeDetails);
148+
.signAndOrEncryptEmail(session, messageToProtect, emailContainingSmimeDetails, defaultSmimeSigningStore);
148149
}
149150
}

0 commit comments

Comments
 (0)