diff --git a/modules/core-module/src/main/java/org/simplejavamail/api/email/EmailPopulatingBuilder.java b/modules/core-module/src/main/java/org/simplejavamail/api/email/EmailPopulatingBuilder.java index 36afe4bdd..685c03aa1 100644 --- a/modules/core-module/src/main/java/org/simplejavamail/api/email/EmailPopulatingBuilder.java +++ b/modules/core-module/src/main/java/org/simplejavamail/api/email/EmailPopulatingBuilder.java @@ -1,12 +1,12 @@ package org.simplejavamail.api.email; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import org.simplejavamail.api.internal.clisupport.model.Cli; import org.simplejavamail.api.internal.clisupport.model.CliBuilderApiType; import org.simplejavamail.api.mailer.config.Pkcs12Config; import javax.activation.DataSource; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; import javax.mail.Message; import javax.mail.internet.InternetAddress; import javax.mail.internet.MimeMessage; @@ -1127,10 +1127,12 @@ public interface EmailPopulatingBuilder { * Signs this email with an S/MIME signature, so the receiving client * can verify whether the email content was tampered with. *

- * Note: this only works in combination with the {@value org.simplejavamail.internal.modules.SMIMEModule#NAME}. + * Note: this only works in combination with the {@value org.simplejavamail.internal.modules.SMIMEModule#NAME}.
+ * Note: You can also configure your Mailer instance do sign all emails by default (also has better performance). * * @see S/MIME on Wikipedia * @see Primer on S/MIME + * @see org.simplejavamail.api.mailer.MailerGenericBuilder#signByDefaultWithSmime(Pkcs12Config) */ @Cli.ExcludeApi(reason = "delegated method contains CLI compatible arguments") EmailPopulatingBuilder signWithSmime(@NotNull Pkcs12Config pkcs12Config); diff --git a/modules/core-module/src/main/java/org/simplejavamail/api/mailer/Mailer.java b/modules/core-module/src/main/java/org/simplejavamail/api/mailer/Mailer.java index e81ff2828..83729f0aa 100644 --- a/modules/core-module/src/main/java/org/simplejavamail/api/mailer/Mailer.java +++ b/modules/core-module/src/main/java/org/simplejavamail/api/mailer/Mailer.java @@ -2,19 +2,19 @@ import org.hazlewood.connor.bottema.emailaddress.EmailAddressCriteria; import org.hazlewood.connor.bottema.emailaddress.EmailAddressValidator; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import org.simplejavamail.MailException; import org.simplejavamail.api.email.Email; +import org.simplejavamail.api.mailer.config.EmailGovernance; import org.simplejavamail.api.mailer.config.OperationalConfig; import org.simplejavamail.api.mailer.config.ProxyConfig; import org.simplejavamail.api.mailer.config.ServerConfig; import org.simplejavamail.api.mailer.config.TransportStrategy; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; import javax.mail.Message; import javax.mail.Session; import javax.mail.Transport; -import java.util.EnumSet; import java.util.concurrent.Future; /** @@ -148,10 +148,8 @@ public interface Mailer { OperationalConfig getOperationalConfig(); /** - * @return The effective validation criteria used for email validation. Returns an empty set if no validation should be done. - * @see MailerGenericBuilder#withEmailAddressCriteria(EnumSet) - * @see EmailAddressCriteria + * @return The effective governance applied to each email (default S/MIME signing, email addresscriteria for validation etc.). */ @NotNull - EnumSet getEmailAddressCriteria(); + EmailGovernance getEmailGovernance(); } diff --git a/modules/core-module/src/main/java/org/simplejavamail/api/mailer/MailerGenericBuilder.java b/modules/core-module/src/main/java/org/simplejavamail/api/mailer/MailerGenericBuilder.java index 84b4c3e0b..47da2ae11 100644 --- a/modules/core-module/src/main/java/org/simplejavamail/api/mailer/MailerGenericBuilder.java +++ b/modules/core-module/src/main/java/org/simplejavamail/api/mailer/MailerGenericBuilder.java @@ -1,14 +1,17 @@ package org.simplejavamail.api.mailer; import org.hazlewood.connor.bottema.emailaddress.EmailAddressCriteria; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import org.simplejavamail.api.internal.clisupport.model.Cli; import org.simplejavamail.api.internal.clisupport.model.CliBuilderApiType; import org.simplejavamail.api.mailer.config.LoadBalancingStrategy; +import org.simplejavamail.api.mailer.config.Pkcs12Config; import org.simplejavamail.api.mailer.config.TransportStrategy; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; import javax.mail.Session; +import java.io.File; +import java.io.InputStream; import java.util.EnumSet; import java.util.List; import java.util.Map; @@ -226,6 +229,53 @@ public interface MailerGenericBuilder> { */ T withEmailAddressCriteria(@NotNull EnumSet emailAddressCriteria); + /** + * Signs this all emails by default with an S/MIME signature, so the receiving client + * can verify whether the email content was tampered with. + *

+ * Note: this only works in combination with the {@value org.simplejavamail.internal.modules.SMIMEModule#NAME}. + * + * @see S/MIME on Wikipedia + * @see Primer on S/MIME + * @see org.simplejavamail.api.email.EmailPopulatingBuilder#signWithSmime(Pkcs12Config) + * @see #clearSignByDefaultWithSmime() + */ + @Cli.ExcludeApi(reason = "delegated method contains CLI compatible arguments") + T signByDefaultWithSmime(@NotNull Pkcs12Config pkcs12Config); + + /** + * Delegates to {@link #signByDefaultWithSmime(InputStream, String, String, String)}. + *

+ * Note: this only works in combination with the {@value org.simplejavamail.internal.modules.SMIMEModule#NAME}. + * + * @param pkcs12StoreFile The key store file to use to find the indicated key + * @param storePassword The store's password + * @param keyAlias The name of the certificate in the key store to use + * @param keyPassword The password of the certificate + */ + T signByDefaultWithSmime(@NotNull File pkcs12StoreFile, @NotNull String storePassword, @NotNull String keyAlias, @NotNull String keyPassword); + + /** + * Delegates to {@link #signByDefaultWithSmime(byte[], String, String, String)}. + *

+ * Note: this only works in combination with the {@value org.simplejavamail.internal.modules.SMIMEModule#NAME}. + */ + @Cli.ExcludeApi(reason = "Is duplicate API from CLI point of view") + T signByDefaultWithSmime(@NotNull InputStream pkcs12StoreStream, @NotNull String storePassword, @NotNull String keyAlias, @NotNull String keyPassword); + + /** + * Delegates to {@link #signByDefaultWithSmime(Pkcs12Config)}. + *

+ * Note: this only works in combination with the {@value org.simplejavamail.internal.modules.SMIMEModule#NAME}. + * + * @param pkcs12StoreData The key store file to use to find the indicated key + * @param storePassword The store's password + * @param keyAlias The name of the certificate in the key store to use + * @param keyPassword The password of the certificate + */ + @Cli.ExcludeApi(reason = "Is duplicate API from CLI point of view") + T signByDefaultWithSmime(@NotNull byte[] pkcs12StoreData, @NotNull String storePassword, @NotNull String keyAlias, @NotNull String keyPassword); + /** * For advanced use cases. *

@@ -580,6 +630,13 @@ public interface MailerGenericBuilder> { */ T clearEmailAddressCriteria(); + /** + * Removes S/MIME signing, so emails won't be signed by default. + * + * @see #signByDefaultWithSmime(Pkcs12Config) + */ + T clearSignByDefaultWithSmime(); + /** * Removes all trusted hosts from the list. * @@ -651,6 +708,13 @@ public interface MailerGenericBuilder> { @Nullable EnumSet getEmailAddressCriteria(); + /** + * @see #signByDefaultWithSmime(Pkcs12Config) + * @see #signByDefaultWithSmime(InputStream, String, String, String) + */ + @Nullable + Pkcs12Config getPkcs12ConfigForSmimeSigning(); + /** * 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 * new {@link Mailer} instance. diff --git a/modules/core-module/src/main/java/org/simplejavamail/api/mailer/config/EmailGovernance.java b/modules/core-module/src/main/java/org/simplejavamail/api/mailer/config/EmailGovernance.java new file mode 100644 index 000000000..0f68041b7 --- /dev/null +++ b/modules/core-module/src/main/java/org/simplejavamail/api/mailer/config/EmailGovernance.java @@ -0,0 +1,36 @@ +package org.simplejavamail.api.mailer.config; + +import org.hazlewood.connor.bottema.emailaddress.EmailAddressCriteria; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.simplejavamail.api.email.EmailPopulatingBuilder; +import org.simplejavamail.api.mailer.MailerGenericBuilder; + +import java.io.InputStream; +import java.util.EnumSet; + +/** + * Governance for all emails being sent through the current {@link org.simplejavamail.api.mailer.Mailer} instance. + *

+ * 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 + * emails by default. You can do it manually on each email of course, but then the keystore used for this not reused. + */ +public interface EmailGovernance { + + /** + * @return The effective validation criteria used for email validation. Returns an empty set if no validation should be done. + * @see MailerGenericBuilder#withEmailAddressCriteria(EnumSet) + * @see EmailAddressCriteria + */ + @NotNull + EnumSet getEmailAddressCriteria(); + + /** + * @see EmailPopulatingBuilder#signWithSmime(Pkcs12Config) + * @see EmailPopulatingBuilder#signWithSmime(InputStream, String, String, String) + * @see MailerGenericBuilder#signByDefaultWithSmime(Pkcs12Config) + * @see MailerGenericBuilder#signByDefaultWithSmime(InputStream, String, String, String) + */ + @Nullable + Pkcs12Config getPkcs12ConfigForSmimeSigning(); +} diff --git a/modules/core-module/src/main/java/org/simplejavamail/internal/modules/SMIMEModule.java b/modules/core-module/src/main/java/org/simplejavamail/internal/modules/SMIMEModule.java index 3f523d2bf..a299169ce 100644 --- a/modules/core-module/src/main/java/org/simplejavamail/internal/modules/SMIMEModule.java +++ b/modules/core-module/src/main/java/org/simplejavamail/internal/modules/SMIMEModule.java @@ -1,5 +1,7 @@ package org.simplejavamail.internal.modules; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import org.simplejavamail.api.email.AttachmentResource; import org.simplejavamail.api.email.Email; import org.simplejavamail.api.email.OriginalSmimeDetails; @@ -9,8 +11,6 @@ import org.simplejavamail.api.internal.smimesupport.model.SmimeDetails; import org.simplejavamail.api.mailer.config.Pkcs12Config; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; import javax.mail.Session; import javax.mail.internet.MimeMessage; import javax.mail.internet.MimePart; @@ -70,7 +70,8 @@ public interface SMIMEModule { boolean verifyValidSignature(@NotNull MimeMessage mimeMessage, @NotNull OriginalSmimeDetails messageSmimeDetails); @NotNull - MimeMessage signAndOrEncryptEmail(@NotNull final Session session, @NotNull final MimeMessage messageToProtect, @NotNull final Email emailContainingSmimeDetails); + MimeMessage signAndOrEncryptEmail(@NotNull final Session session, @NotNull final MimeMessage messageToProtect, @NotNull final Email emailContainingSmimeDetails, + @Nullable final Pkcs12Config defaultSmimeSigningStore); @NotNull MimeMessage signMessage(@Nullable Session session, @NotNull MimeMessage message, @NotNull Pkcs12Config pkcs12Config); diff --git a/modules/simple-java-mail/src/main/java/org/simplejavamail/converter/EmailConverter.java b/modules/simple-java-mail/src/main/java/org/simplejavamail/converter/EmailConverter.java index 197055915..d2a78044c 100644 --- a/modules/simple-java-mail/src/main/java/org/simplejavamail/converter/EmailConverter.java +++ b/modules/simple-java-mail/src/main/java/org/simplejavamail/converter/EmailConverter.java @@ -428,11 +428,29 @@ public static MimeMessage emailToMimeMessage(@NotNull final Email email) { } /** - * Refer to {@link MimeMessageProducerHelper#produceMimeMessage(Email, Session)}. + * Refer to {@link MimeMessageProducerHelper#produceMimeMessage(Email, Session, Pkcs12Config)}. + */ + public static MimeMessage emailToMimeMessage(@NotNull final Email email, @NotNull final Session session, @NotNull final Pkcs12Config defaultSmimeSigningStore) { + try { + return MimeMessageProducerHelper.produceMimeMessage( + checkNonEmptyArgument(email, "email"), + checkNonEmptyArgument(session, "session"), + checkNonEmptyArgument(defaultSmimeSigningStore, "defaultSmimeSigningStore")); + } catch (UnsupportedEncodingException | MessagingException e) { + // this should never happen, so we don't acknowledge this exception (and simply bubble up) + throw new IllegalStateException(e.getMessage(), e); + } + } + + /** + * Delegates to {@link MimeMessageProducerHelper#produceMimeMessage(Email, Session, Pkcs12Config)} with empty S/MIME signing store. */ public static MimeMessage emailToMimeMessage(@NotNull final Email email, @NotNull final Session session) { try { - return MimeMessageProducerHelper.produceMimeMessage(checkNonEmptyArgument(email, "email"), checkNonEmptyArgument(session, "session")); + return MimeMessageProducerHelper.produceMimeMessage( + checkNonEmptyArgument(email, "email"), + checkNonEmptyArgument(session, "session"), + null); } catch (UnsupportedEncodingException | MessagingException e) { // this should never happen, so we don't acknowledge this exception (and simply bubble up) throw new IllegalStateException(e.getMessage(), e); diff --git a/modules/simple-java-mail/src/main/java/org/simplejavamail/converter/internal/mimemessage/MimeMessageProducer.java b/modules/simple-java-mail/src/main/java/org/simplejavamail/converter/internal/mimemessage/MimeMessageProducer.java index 9fe2bd889..ff35684de 100644 --- a/modules/simple-java-mail/src/main/java/org/simplejavamail/converter/internal/mimemessage/MimeMessageProducer.java +++ b/modules/simple-java-mail/src/main/java/org/simplejavamail/converter/internal/mimemessage/MimeMessageProducer.java @@ -1,10 +1,11 @@ package org.simplejavamail.converter.internal.mimemessage; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import org.simplejavamail.api.email.Email; +import org.simplejavamail.api.mailer.config.Pkcs12Config; import org.simplejavamail.internal.modules.ModuleLoader; -import org.jetbrains.annotations.NotNull; - import javax.mail.MessagingException; import javax.mail.Session; import javax.mail.internet.MimeMessage; @@ -37,7 +38,7 @@ public abstract class MimeMessageProducer { */ abstract boolean compatibleWithEmail(@NotNull Email email); - final MimeMessage populateMimeMessage(@NotNull final Email email, @NotNull Session session) + final MimeMessage populateMimeMessage(@NotNull final Email email, @NotNull Session session, @Nullable final Pkcs12Config defaultSmimeSigningStore) throws MessagingException, UnsupportedEncodingException { checkArgumentNotEmpty(email, "email is missing"); checkArgumentNotEmpty(session, "session is needed, it cannot be attached later"); @@ -80,7 +81,7 @@ public String toString() { 3. DKIM signing */ if (ModuleLoader.smimeModuleAvailable()) { - message = ModuleLoader.loadSmimeModule().signAndOrEncryptEmail(session, message, email); + message = ModuleLoader.loadSmimeModule().signAndOrEncryptEmail(session, message, email, defaultSmimeSigningStore); } if (!valueNullOrEmpty(email.getDkimSigningDomain())) { diff --git a/modules/simple-java-mail/src/main/java/org/simplejavamail/converter/internal/mimemessage/MimeMessageProducerHelper.java b/modules/simple-java-mail/src/main/java/org/simplejavamail/converter/internal/mimemessage/MimeMessageProducerHelper.java index 136597568..3cd10a3f8 100644 --- a/modules/simple-java-mail/src/main/java/org/simplejavamail/converter/internal/mimemessage/MimeMessageProducerHelper.java +++ b/modules/simple-java-mail/src/main/java/org/simplejavamail/converter/internal/mimemessage/MimeMessageProducerHelper.java @@ -1,8 +1,10 @@ package org.simplejavamail.converter.internal.mimemessage; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import org.simplejavamail.api.email.Email; +import org.simplejavamail.api.mailer.config.Pkcs12Config; -import org.jetbrains.annotations.NotNull; import javax.mail.MessagingException; import javax.mail.Session; import javax.mail.internet.MimeMessage; @@ -34,10 +36,10 @@ public final class MimeMessageProducerHelper { private MimeMessageProducerHelper() { } - public static MimeMessage produceMimeMessage(@NotNull Email email, @NotNull Session session) throws UnsupportedEncodingException, MessagingException { + public static MimeMessage produceMimeMessage(@NotNull Email email, @NotNull Session session, @Nullable final Pkcs12Config defaultSmimeSigningStore) throws UnsupportedEncodingException, MessagingException { for (MimeMessageProducer mimeMessageProducer : mimeMessageProducers) { if (mimeMessageProducer.compatibleWithEmail(email)) { - return mimeMessageProducer.populateMimeMessage(email, session); + return mimeMessageProducer.populateMimeMessage(email, session, defaultSmimeSigningStore); } } throw new IllegalStateException("no compatible MimeMessageProducer found for email"); diff --git a/modules/simple-java-mail/src/main/java/org/simplejavamail/email/internal/EmailPopulatingBuilderImpl.java b/modules/simple-java-mail/src/main/java/org/simplejavamail/email/internal/EmailPopulatingBuilderImpl.java index 6a887c9ab..06123d4ca 100644 --- a/modules/simple-java-mail/src/main/java/org/simplejavamail/email/internal/EmailPopulatingBuilderImpl.java +++ b/modules/simple-java-mail/src/main/java/org/simplejavamail/email/internal/EmailPopulatingBuilderImpl.java @@ -73,10 +73,6 @@ import static org.simplejavamail.config.ConfigLoader.Property.EMBEDDEDIMAGES_DYNAMICRESOLUTION_OUTSIDE_BASE_DIR; import static org.simplejavamail.config.ConfigLoader.Property.EMBEDDEDIMAGES_DYNAMICRESOLUTION_OUTSIDE_BASE_URL; import static org.simplejavamail.config.ConfigLoader.Property.SMIME_ENCRYPTION_CERTIFICATE; -import static org.simplejavamail.config.ConfigLoader.Property.SMIME_SIGNING_KEYSTORE; -import static org.simplejavamail.config.ConfigLoader.Property.SMIME_SIGNING_KEYSTORE_PASSWORD; -import static org.simplejavamail.config.ConfigLoader.Property.SMIME_SIGNING_KEY_ALIAS; -import static org.simplejavamail.config.ConfigLoader.Property.SMIME_SIGNING_KEY_PASSWORD; import static org.simplejavamail.config.ConfigLoader.getBooleanProperty; import static org.simplejavamail.config.ConfigLoader.getProperty; import static org.simplejavamail.config.ConfigLoader.getStringProperty; @@ -366,14 +362,6 @@ public class EmailPopulatingBuilderImpl implements InternalEmailPopulatingBuilde if (hasProperty(DEFAULT_SUBJECT)) { withSubject((String) getProperty(DEFAULT_SUBJECT)); } - if (hasProperty(SMIME_SIGNING_KEYSTORE)) { - signWithSmime(Pkcs12Config.builder() - .pkcs12Store(assumeNonNull(getStringProperty(SMIME_SIGNING_KEYSTORE))) - .storePassword(checkNonEmptyArgument(getStringProperty(SMIME_SIGNING_KEYSTORE_PASSWORD), "Keystore password property")) - .keyAlias(checkNonEmptyArgument(getStringProperty(SMIME_SIGNING_KEY_ALIAS), "Key alias property")) - .keyPassword(checkNonEmptyArgument(getStringProperty(SMIME_SIGNING_KEY_PASSWORD), "Key password property")) - .build()); - } if (hasProperty(SMIME_ENCRYPTION_CERTIFICATE)) { encryptWithSmime(assumeNonNull(getStringProperty(SMIME_ENCRYPTION_CERTIFICATE))); } diff --git a/modules/simple-java-mail/src/main/java/org/simplejavamail/mailer/MailerHelper.java b/modules/simple-java-mail/src/main/java/org/simplejavamail/mailer/MailerHelper.java index cc8065b6c..9acc4414b 100644 --- a/modules/simple-java-mail/src/main/java/org/simplejavamail/mailer/MailerHelper.java +++ b/modules/simple-java-mail/src/main/java/org/simplejavamail/mailer/MailerHelper.java @@ -8,6 +8,7 @@ import org.simplejavamail.api.email.AttachmentResource; import org.simplejavamail.api.email.Email; import org.simplejavamail.api.email.Recipient; +import org.simplejavamail.api.mailer.config.Pkcs12Config; import org.simplejavamail.internal.modules.ModuleLoader; import org.slf4j.Logger; @@ -139,11 +140,11 @@ public static MimeMessage signMessageWithDKIM(@NotNull final MimeMessage message /** * Depending on the Email configuration, signs and then encrypts message (both steps optional), using the S/MIME module. * - * @see org.simplejavamail.internal.modules.SMIMEModule#signAndOrEncryptEmail(Session, MimeMessage, Email) + * @see org.simplejavamail.internal.modules.SMIMEModule#signAndOrEncryptEmail(Session, MimeMessage, Email, Pkcs12Config) */ @SuppressWarnings("unused") - public static MimeMessage signAndOrEncryptMessageWithSmime(@NotNull final Session session, @NotNull final MimeMessage messageToProtect, @NotNull final Email emailContainingSmimeDetails) { + public static MimeMessage signAndOrEncryptMessageWithSmime(@NotNull final Session session, @NotNull final MimeMessage messageToProtect, @NotNull final Email emailContainingSmimeDetails, @Nullable final Pkcs12Config defaultSmimeSigningStore) { return ModuleLoader.loadSmimeModule() - .signAndOrEncryptEmail(session, messageToProtect, emailContainingSmimeDetails); + .signAndOrEncryptEmail(session, messageToProtect, emailContainingSmimeDetails, defaultSmimeSigningStore); } } diff --git a/modules/simple-java-mail/src/main/java/org/simplejavamail/mailer/internal/EmailGovernanceImpl.java b/modules/simple-java-mail/src/main/java/org/simplejavamail/mailer/internal/EmailGovernanceImpl.java new file mode 100644 index 000000000..aa80223f6 --- /dev/null +++ b/modules/simple-java-mail/src/main/java/org/simplejavamail/mailer/internal/EmailGovernanceImpl.java @@ -0,0 +1,47 @@ +package org.simplejavamail.mailer.internal; + +import org.hazlewood.connor.bottema.emailaddress.EmailAddressCriteria; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.simplejavamail.api.mailer.config.EmailGovernance; +import org.simplejavamail.api.mailer.config.Pkcs12Config; + +import java.util.EnumSet; + +/** + * @see EmailGovernance + */ +class EmailGovernanceImpl implements EmailGovernance { + @NotNull private final EnumSet emailAddressCriteria; + @Nullable private final Pkcs12Config pkcs12ConfigForSmimeSigning; + + EmailGovernanceImpl(@NotNull final EnumSet emailAddressCriteria, @Nullable final Pkcs12Config pkcs12ConfigForSmimeSigning) { + this.emailAddressCriteria = emailAddressCriteria; + this.pkcs12ConfigForSmimeSigning = pkcs12ConfigForSmimeSigning; + } + + @Override + public String toString() { + final StringBuilder sb = new StringBuilder("EmailGovernanceImpl{"); + sb.append("emailAddressCriteria=").append(emailAddressCriteria); + sb.append(", pkcs12ConfigForSmimeSigning=").append(pkcs12ConfigForSmimeSigning); + sb.append('}'); + return sb.toString(); + } + + /** + * @see EmailGovernance#getEmailAddressCriteria() + */ + @Override + public @NotNull EnumSet getEmailAddressCriteria() { + return emailAddressCriteria; + } + + /** + * @see EmailGovernance#getPkcs12ConfigForSmimeSigning() + */ + @Override + public @Nullable Pkcs12Config getPkcs12ConfigForSmimeSigning() { + return pkcs12ConfigForSmimeSigning; + } +} \ No newline at end of file diff --git a/modules/simple-java-mail/src/main/java/org/simplejavamail/mailer/internal/MailerException.java b/modules/simple-java-mail/src/main/java/org/simplejavamail/mailer/internal/MailerException.java index 815d6dc5c..aeee4e1c3 100644 --- a/modules/simple-java-mail/src/main/java/org/simplejavamail/mailer/internal/MailerException.java +++ b/modules/simple-java-mail/src/main/java/org/simplejavamail/mailer/internal/MailerException.java @@ -8,6 +8,8 @@ @SuppressWarnings("serial") class MailerException extends MailException { + static final String ERROR_READING_SMIME_FROM_INPUTSTREAM = "Was unable to read S/MIME data from input stream"; + static final String ERROR_READING_FROM_FILE = "Error reading from file: %s"; static final String INVALID_PROXY_SLL_COMBINATION = "Proxy is not supported for SSL connections (this is a limitation by the underlying JavaMail framework)"; static final String GENERIC_ERROR = "Third party error"; static final String INVALID_ENCODING = "Encoding not accepted"; diff --git a/modules/simple-java-mail/src/main/java/org/simplejavamail/mailer/internal/MailerGenericBuilderImpl.java b/modules/simple-java-mail/src/main/java/org/simplejavamail/mailer/internal/MailerGenericBuilderImpl.java index a4eb24739..d72cd5b9e 100644 --- a/modules/simple-java-mail/src/main/java/org/simplejavamail/mailer/internal/MailerGenericBuilderImpl.java +++ b/modules/simple-java-mail/src/main/java/org/simplejavamail/mailer/internal/MailerGenericBuilderImpl.java @@ -1,16 +1,23 @@ package org.simplejavamail.mailer.internal; +import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import org.hazlewood.connor.bottema.emailaddress.EmailAddressCriteria; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import org.simplejavamail.api.mailer.CustomMailer; import org.simplejavamail.api.mailer.MailerGenericBuilder; +import org.simplejavamail.api.mailer.config.EmailGovernance; import org.simplejavamail.api.mailer.config.LoadBalancingStrategy; import org.simplejavamail.api.mailer.config.OperationalConfig; +import org.simplejavamail.api.mailer.config.Pkcs12Config; import org.simplejavamail.api.mailer.config.ProxyConfig; import org.simplejavamail.config.ConfigLoader.Property; import org.simplejavamail.internal.modules.ModuleLoader; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; import java.util.ArrayList; import java.util.Arrays; import java.util.EnumSet; @@ -21,10 +28,15 @@ import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; +import static java.lang.String.format; import static org.simplejavamail.config.ConfigLoader.Property.DEFAULT_CONNECTIONPOOL_CLUSTER_KEY; import static org.simplejavamail.config.ConfigLoader.Property.PROXY_HOST; import static org.simplejavamail.config.ConfigLoader.Property.PROXY_PASSWORD; import static org.simplejavamail.config.ConfigLoader.Property.PROXY_USERNAME; +import static org.simplejavamail.config.ConfigLoader.Property.SMIME_SIGNING_KEYSTORE; +import static org.simplejavamail.config.ConfigLoader.Property.SMIME_SIGNING_KEYSTORE_PASSWORD; +import static org.simplejavamail.config.ConfigLoader.Property.SMIME_SIGNING_KEY_ALIAS; +import static org.simplejavamail.config.ConfigLoader.Property.SMIME_SIGNING_KEY_PASSWORD; import static org.simplejavamail.config.ConfigLoader.getStringProperty; import static org.simplejavamail.config.ConfigLoader.hasProperty; import static org.simplejavamail.config.ConfigLoader.valueOrProperty; @@ -32,8 +44,12 @@ import static org.simplejavamail.config.ConfigLoader.valueOrPropertyAsInteger; import static org.simplejavamail.config.ConfigLoader.valueOrPropertyAsString; import static org.simplejavamail.internal.util.MiscUtil.checkArgumentNotEmpty; +import static org.simplejavamail.internal.util.MiscUtil.readInputStreamToBytes; import static org.simplejavamail.internal.util.MiscUtil.valueNullOrEmpty; import static org.simplejavamail.internal.util.Preconditions.assumeNonNull; +import static org.simplejavamail.internal.util.Preconditions.checkNonEmptyArgument; +import static org.simplejavamail.mailer.internal.MailerException.ERROR_READING_FROM_FILE; +import static org.simplejavamail.mailer.internal.MailerException.ERROR_READING_SMIME_FROM_INPUTSTREAM; /** * @see MailerGenericBuilder @@ -82,13 +98,19 @@ abstract class MailerGenericBuilderImpl> i */ @NotNull private Integer sessionTimeout; - + /** * @see MailerGenericBuilder#withEmailAddressCriteria(EnumSet) */ @NotNull private EnumSet emailAddressCriteria; + /** + * @see MailerGenericBuilder#signByDefaultWithSmime(Pkcs12Config) + */ + @Nullable + private Pkcs12Config pkcs12ConfigForSmimeSigning; + /** * @see MailerGenericBuilder#withExecutorService(ExecutorService) */ @@ -216,6 +238,15 @@ abstract class MailerGenericBuilderImpl> i } this.emailAddressCriteria = EmailAddressCriteria.RFC_COMPLIANT.clone(); + + if (hasProperty(SMIME_SIGNING_KEYSTORE)) { + signByDefaultWithSmime(Pkcs12Config.builder() + .pkcs12Store(assumeNonNull(getStringProperty(SMIME_SIGNING_KEYSTORE))) + .storePassword(checkNonEmptyArgument(getStringProperty(SMIME_SIGNING_KEYSTORE_PASSWORD), "Keystore password property")) + .keyAlias(checkNonEmptyArgument(getStringProperty(SMIME_SIGNING_KEY_ALIAS), "Key alias property")) + .keyPassword(checkNonEmptyArgument(getStringProperty(SMIME_SIGNING_KEY_PASSWORD), "Key password property")) + .build()); + } } /** @@ -241,6 +272,13 @@ private void validateProxy() { } } } + + /** + * For internal use. + */ + EmailGovernance buildEmailGovernance() { + return new EmailGovernanceImpl(assumeNonNull(getEmailAddressCriteria()), getPkcs12ConfigForSmimeSigning()); + } /** * For internal use. @@ -369,6 +407,70 @@ public T withEmailAddressCriteria(@NotNull final EnumSet e return (T) this; } + /** + * @param pkcs12StoreFile The file containing the keystore + * @param storePassword The password to get keys from the store + * @param keyAlias The key we need for signing + * @param keyPassword The password for the key + * + * @see MailerGenericBuilder#signByDefaultWithSmime(File, String, String, String) + */ + @Override + @SuppressFBWarnings(value = "OBL_UNSATISFIED_OBLIGATION", justification = "Input stream being created should not be closed here") + public T signByDefaultWithSmime(@NotNull final File pkcs12StoreFile, @NotNull final String storePassword, @NotNull final String keyAlias, @NotNull final String keyPassword) { + try { + return signByDefaultWithSmime(new FileInputStream(pkcs12StoreFile), storePassword, keyAlias, keyPassword); + } catch (IOException e) { + throw new MailerException(format(ERROR_READING_FROM_FILE, pkcs12StoreFile), e); + } + } + + /** + * @param pkcs12StoreStream The data (file) input stream containing the keystore + * @param storePassword The password to get keys from the store + * @param keyAlias The key we need for signing + * @param keyPassword The password for the key + * + * @see MailerGenericBuilder#signByDefaultWithSmime(InputStream, String, String, String) + */ + @Override + public T signByDefaultWithSmime(@NotNull final InputStream pkcs12StoreStream, @NotNull final String storePassword, @NotNull final String keyAlias, @NotNull final String keyPassword) { + final byte[] pkcs12StoreData; + try { + pkcs12StoreData = readInputStreamToBytes(pkcs12StoreStream); + } catch (IOException e) { + throw new MailerException(ERROR_READING_SMIME_FROM_INPUTSTREAM, e); + } + return signByDefaultWithSmime(pkcs12StoreData, storePassword, keyAlias, keyPassword); + } + + /** + * @param pkcs12StoreData The data (file) input stream containing the keystore + * @param storePassword The password to get keys from the store + * @param keyAlias The key we need for signing + * @param keyPassword The password for the key + * + * @see MailerGenericBuilder#signByDefaultWithSmime(InputStream, String, String, String) + */ + @Override + public T signByDefaultWithSmime(@NotNull final byte[] pkcs12StoreData, @NotNull final String storePassword, @NotNull final String keyAlias, @NotNull final String keyPassword) { + return signByDefaultWithSmime(Pkcs12Config.builder() + .pkcs12Store(pkcs12StoreData) + .storePassword(storePassword) + .keyAlias(keyAlias) + .keyPassword(keyPassword) + .build()); + } + + /** + * @see MailerGenericBuilder#signByDefaultWithSmime(Pkcs12Config) + */ + @Override + public T signByDefaultWithSmime(@NotNull final Pkcs12Config pkcs12Config) { + this.pkcs12ConfigForSmimeSigning = pkcs12Config; + return (T) this; + } + /** * @see MailerGenericBuilder#withExecutorService(ExecutorService) */ @@ -658,7 +760,7 @@ public T clearProxy() { return (T) withProxy(null, null, null, null) .withProxyBridgePort(DEFAULT_PROXY_BRIDGE_PORT); } - + /** * @see MailerGenericBuilder#clearEmailAddressCriteria() */ @@ -666,6 +768,15 @@ public T clearProxy() { public T clearEmailAddressCriteria() { return withEmailAddressCriteria(EnumSet.noneOf(EmailAddressCriteria.class)); } + + /** + * @see MailerGenericBuilder#clearSignByDefaultWithSmime() + */ + @Override + public T clearSignByDefaultWithSmime() { + this.pkcs12ConfigForSmimeSigning = null; + return (T) this; + } /** * @see MailerGenericBuilder#clearTrustedSSLHosts() @@ -763,6 +874,15 @@ public EnumSet getEmailAddressCriteria() { return emailAddressCriteria; } + /** + * @see MailerGenericBuilder#getPkcs12ConfigForSmimeSigning() + */ + @Override + @Nullable + public Pkcs12Config getPkcs12ConfigForSmimeSigning() { + return pkcs12ConfigForSmimeSigning; + } + /** * @see MailerGenericBuilder#getExecutorService() */ diff --git a/modules/simple-java-mail/src/main/java/org/simplejavamail/mailer/internal/MailerImpl.java b/modules/simple-java-mail/src/main/java/org/simplejavamail/mailer/internal/MailerImpl.java index be04bd7f7..9b5941f7d 100644 --- a/modules/simple-java-mail/src/main/java/org/simplejavamail/mailer/internal/MailerImpl.java +++ b/modules/simple-java-mail/src/main/java/org/simplejavamail/mailer/internal/MailerImpl.java @@ -1,6 +1,5 @@ package org.simplejavamail.mailer.internal; -import org.hazlewood.connor.bottema.emailaddress.EmailAddressCriteria; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.simplejavamail.MailException; @@ -8,6 +7,7 @@ import org.simplejavamail.api.internal.authenticatedsockssupport.socks5server.AnonymousSocks5Server; import org.simplejavamail.api.mailer.AsyncResponse; import org.simplejavamail.api.mailer.Mailer; +import org.simplejavamail.api.mailer.config.EmailGovernance; import org.simplejavamail.api.mailer.config.OperationalConfig; import org.simplejavamail.api.mailer.config.ProxyConfig; import org.simplejavamail.api.mailer.config.ServerConfig; @@ -72,7 +72,7 @@ public class MailerImpl implements Mailer { * @see org.simplejavamail.api.mailer.MailerGenericBuilder#withEmailAddressCriteria(EnumSet) */ @NotNull - private final EnumSet emailAddressCriteria; + private final EmailGovernance emailGovernance; /** * @see org.simplejavamail.api.mailer.MailerRegularBuilder#withTransportStrategy(TransportStrategy) @@ -95,7 +95,7 @@ public class MailerImpl implements Mailer { MailerImpl(@NotNull final MailerFromSessionBuilderImpl fromSessionBuilder) { this(null, null, - fromSessionBuilder.getEmailAddressCriteria(), + fromSessionBuilder.buildEmailGovernance(), fromSessionBuilder.buildProxyConfig(), fromSessionBuilder.getSession(), fromSessionBuilder.buildOperationalConfig()); @@ -104,17 +104,17 @@ public class MailerImpl implements Mailer { MailerImpl(@NotNull final MailerRegularBuilderImpl regularBuilder) { this(regularBuilder.buildServerConfig(), regularBuilder.getTransportStrategy(), - regularBuilder.getEmailAddressCriteria(), + regularBuilder.buildEmailGovernance(), regularBuilder.buildProxyConfig(), null, regularBuilder.buildOperationalConfig()); } - MailerImpl(@Nullable ServerConfig serverConfig, @Nullable TransportStrategy transportStrategy, @NotNull EnumSet emailAddressCriteria, @NotNull ProxyConfig proxyConfig, + MailerImpl(@Nullable ServerConfig serverConfig, @Nullable TransportStrategy transportStrategy, @NotNull EmailGovernance emailGovernance, @NotNull ProxyConfig proxyConfig, @Nullable Session session, @NotNull OperationalConfig operationalConfig) { this.serverConfig = serverConfig; this.transportStrategy = transportStrategy; - this.emailAddressCriteria = emailAddressCriteria; + this.emailGovernance = emailGovernance; this.proxyConfig = proxyConfig; if (session == null) { session = createMailSession(checkNonEmptyArgument(serverConfig, "serverConfig"), checkNonEmptyArgument(transportStrategy, "transportStrategy")); @@ -323,7 +323,7 @@ public final void sendMail(final Email email) { @Nullable public final AsyncResponse sendMail(final Email email, @SuppressWarnings("SameParameterValue") final boolean async) { if (validate(email)) { - SendMailClosure sendMailClosure = new SendMailClosure(operationalConfig, session, email, proxyServer, async, operationalConfig.isTransportModeLoggingOnly(), + SendMailClosure sendMailClosure = new SendMailClosure(operationalConfig, emailGovernance, session, email, proxyServer, async, operationalConfig.isTransportModeLoggingOnly(), smtpConnectionCounter); if (!async) { @@ -346,7 +346,7 @@ public final AsyncResponse sendMail(final Email email, @SuppressWarnings("SamePa @SuppressWarnings({"SameReturnValue"}) public boolean validate(@NotNull final Email email) throws MailException { - return MailerHelper.validate(email, emailAddressCriteria); + return MailerHelper.validate(email, emailGovernance.getEmailAddressCriteria()); } /** @@ -407,11 +407,11 @@ public OperationalConfig getOperationalConfig() { } /** - * @see Mailer#getEmailAddressCriteria() + * @see Mailer#getEmailGovernance() */ @Override @NotNull - public EnumSet getEmailAddressCriteria() { - return emailAddressCriteria; + public EmailGovernance getEmailGovernance() { + return emailGovernance; } } \ No newline at end of file diff --git a/modules/simple-java-mail/src/main/java/org/simplejavamail/mailer/internal/MailerRegularBuilderImpl.java b/modules/simple-java-mail/src/main/java/org/simplejavamail/mailer/internal/MailerRegularBuilderImpl.java index 005bf0479..2eaa9b3d0 100644 --- a/modules/simple-java-mail/src/main/java/org/simplejavamail/mailer/internal/MailerRegularBuilderImpl.java +++ b/modules/simple-java-mail/src/main/java/org/simplejavamail/mailer/internal/MailerRegularBuilderImpl.java @@ -193,14 +193,14 @@ public MailerRegularBuilderImpl withCustomSSLFactoryInstance(@Nullable final SSL public Mailer buildMailer() { return new MailerImpl(this); } - + /** * For internal use. */ ServerConfig buildServerConfig() { vallidateServerConfig(); final int serverPort = SimpleOptional.ofNullable(port).orElse(transportStrategy.getDefaultServerPort()); - return new ServerConfigImpl(assumeNonNull(getHost()), serverPort, getUsername(), getPassword(), customSSLFactory, customSSLFactoryInstance); + return new ServerConfigImpl(assumeNonNull(getHost()), serverPort, username, password, customSSLFactory, customSSLFactoryInstance); } private void vallidateServerConfig() { diff --git a/modules/simple-java-mail/src/main/java/org/simplejavamail/mailer/internal/SendMailClosure.java b/modules/simple-java-mail/src/main/java/org/simplejavamail/mailer/internal/SendMailClosure.java index 53c67479a..6b1070fe1 100644 --- a/modules/simple-java-mail/src/main/java/org/simplejavamail/mailer/internal/SendMailClosure.java +++ b/modules/simple-java-mail/src/main/java/org/simplejavamail/mailer/internal/SendMailClosure.java @@ -1,14 +1,15 @@ package org.simplejavamail.mailer.internal; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import org.simplejavamail.api.email.Email; import org.simplejavamail.api.internal.authenticatedsockssupport.socks5server.AnonymousSocks5Server; +import org.simplejavamail.api.mailer.config.EmailGovernance; import org.simplejavamail.api.mailer.config.OperationalConfig; import org.simplejavamail.converter.internal.mimemessage.MimeMessageProducerHelper; import org.simplejavamail.mailer.internal.util.SessionLogger; import org.simplejavamail.mailer.internal.util.TransportRunner; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; import javax.mail.MessagingException; import javax.mail.Session; import javax.mail.internet.MimeMessage; @@ -26,15 +27,18 @@ class SendMailClosure extends AbstractProxyServerSyncingClosure { @NotNull private final OperationalConfig operationalConfig; + @NotNull private final EmailGovernance emailGovernance; @NotNull private final Session session; @NotNull private final Email email; private final boolean asyncForLoggingPurpose; private final boolean transportModeLoggingOnly; - SendMailClosure(@NotNull OperationalConfig operationalConfig, @NotNull Session session, @NotNull Email email, @Nullable AnonymousSocks5Server proxyServer, boolean asyncForLoggingPurpose, + SendMailClosure(@NotNull OperationalConfig operationalConfig, @NotNull EmailGovernance emailGovernance, @NotNull Session session, @NotNull Email email, @Nullable AnonymousSocks5Server proxyServer, + boolean asyncForLoggingPurpose, boolean transportModeLoggingOnly, @NotNull AtomicInteger smtpConnectionCounter) { super(smtpConnectionCounter, proxyServer); this.operationalConfig = operationalConfig; + this.emailGovernance = emailGovernance; this.session = session; this.email = email; this.asyncForLoggingPurpose = asyncForLoggingPurpose; @@ -47,7 +51,7 @@ public void executeClosure() { LOGGER.trace("sending email..."); try { // fill and send wrapped mime message parts - final MimeMessage message = MimeMessageProducerHelper.produceMimeMessage(email, session); + final MimeMessage message = MimeMessageProducerHelper.produceMimeMessage(email, session, emailGovernance.getPkcs12ConfigForSmimeSigning()); SessionLogger.logSession(session, asyncForLoggingPurpose, "mail"); message.saveChanges(); // some headers and id's will be set for this specific message diff --git a/modules/simple-java-mail/src/test/java/org/simplejavamail/mailer/MailerLiveTest.java b/modules/simple-java-mail/src/test/java/org/simplejavamail/mailer/MailerLiveTest.java index 6371a2cdd..7bdf4cbdb 100644 --- a/modules/simple-java-mail/src/test/java/org/simplejavamail/mailer/MailerLiveTest.java +++ b/modules/simple-java-mail/src/test/java/org/simplejavamail/mailer/MailerLiveTest.java @@ -131,6 +131,37 @@ public void createMailSession_OutlookMessageSmimeSignTest() .build()); } + @Test + public void createMailSession_OutlookMessageDefaultSmimeSignTest() + throws IOException, MessagingException, ExecutionException, InterruptedException { + // override the default from the @Before test + mailer = MailerBuilder + .withSMTPServer("localhost", SERVER_PORT) + .signByDefaultWithSmime(new File(RESOURCES_PKCS + "/smime_keystore.pkcs12"), "letmein", "smime_test_user_alias", "letmein") + .buildMailer(); + + EmailPopulatingBuilder builder = readOutlookMessage("test-messages/HTML mail with replyto and attachment and embedded image.msg"); + Email email = assertSendingEmail(builder, false, true, false, true, false); + + // verify that S/MIME was indeed only configured on the mailer instance + assertThat(mailer.getEmailGovernance().getPkcs12ConfigForSmimeSigning()).isNotNull(); + assertThat(builder.getPkcs12ConfigForSmimeSigning()).isNull(); + assertThat(email.getPkcs12ConfigForSmimeSigning()).isNull(); + + verifyReceivedOutlookEmail(email, true, false); + + EmailAssert.assertThat(email).wasNotMergedWithSmimeSignedMessage(); + + EmailAssert.assertThat(email).hasOriginalSmimeDetails(OriginalSmimeDetailsImpl.builder() + .smimeMode(SmimeMode.SIGNED) + .smimeMime("multipart/signed") + .smimeProtocol("application/pkcs7-signature") + .smimeMicalg("sha-256") + .smimeSignedBy("Benny Bottema") + .smimeSignatureValid(true) + .build()); + } + @Test public void testOutlookMessageWithNestedOutlookMessageAttachment() throws IOException { diff --git a/modules/simple-java-mail/src/test/java/org/simplejavamail/mailer/internal/MailerImplTest.java b/modules/simple-java-mail/src/test/java/org/simplejavamail/mailer/internal/MailerImplTest.java index 9c38a3f08..763128aae 100644 --- a/modules/simple-java-mail/src/test/java/org/simplejavamail/mailer/internal/MailerImplTest.java +++ b/modules/simple-java-mail/src/test/java/org/simplejavamail/mailer/internal/MailerImplTest.java @@ -1,12 +1,13 @@ package org.simplejavamail.mailer.internal; import org.hazlewood.connor.bottema.emailaddress.EmailAddressCriteria; +import org.jetbrains.annotations.NotNull; import org.junit.Before; import org.junit.Test; -import org.simplejavamail.api.mailer.config.OperationalConfig; +import org.simplejavamail.api.mailer.Mailer; +import org.simplejavamail.api.mailer.config.EmailGovernance; import org.simplejavamail.api.mailer.config.ProxyConfig; -import org.jetbrains.annotations.NotNull; import javax.mail.Session; import java.util.Arrays; import java.util.Collections; @@ -14,15 +15,13 @@ import java.util.Properties; import static java.util.EnumSet.noneOf; -import static java.util.UUID.randomUUID; -import static java.util.concurrent.Executors.newSingleThreadExecutor; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; -import static org.simplejavamail.api.mailer.config.LoadBalancingStrategy.ROUND_ROBIN; import static org.simplejavamail.api.mailer.config.TransportStrategy.SMTP; import static org.simplejavamail.api.mailer.config.TransportStrategy.SMTPS; import static org.simplejavamail.api.mailer.config.TransportStrategy.SMTP_TLS; +import static org.simplejavamail.util.TestDataHelper.loadPkcs12KeyStore; import static testutil.EmailHelper.createDummyOperationalConfig; public class MailerImplTest { @@ -36,18 +35,13 @@ public void setup() { session = Session.getInstance(new Properties()); } - @NotNull - private ProxyConfig createEmptyProxyConfig() { - return new ProxyConfigImpl(null, null, null, null, null); - } - @Test public void trustAllHosts_PLAIN() { - new MailerImpl(null, SMTP, noneOf(EmailAddressCriteria.class), createEmptyProxyConfig(), session, createDummyOperationalConfig(EMPTY_LIST, true, false)); + new MailerImpl(null, SMTP, createDummyEmailGovernance(), createEmptyProxyConfig(), session, createDummyOperationalConfig(EMPTY_LIST, true, false)); assertThat(session.getProperties().getProperty("mail.smtp.ssl.trust")).isEqualTo("*"); assertThat(session.getProperties().getProperty("mail.smtp.ssl.checkserveridentity")).isNull(); assertThat(session.getProperties().getProperty("mail.smtps.ssl.checkserveridentity")).isNull(); - new MailerImpl(null, SMTP, noneOf(EmailAddressCriteria.class), createEmptyProxyConfig(), session, createDummyOperationalConfig(EMPTY_LIST, false, true)); + new MailerImpl(null, SMTP, createDummyEmailGovernance(), createEmptyProxyConfig(), session, createDummyOperationalConfig(EMPTY_LIST, false, true)); assertThat(session.getProperties().getProperty("mail.smtp.ssl.trust")).isNull(); assertThat(session.getProperties().getProperty("mail.smtp.ssl.checkserveridentity")).isNull(); assertThat(session.getProperties().getProperty("mail.smtps.ssl.checkserveridentity")).isNull(); @@ -57,11 +51,11 @@ public void trustAllHosts_PLAIN() { public void trustAllHosts_SMTPS() { ProxyConfig proxyBypassingMock = mock(ProxyConfig.class); when(proxyBypassingMock.requiresProxy()).thenReturn(false); - new MailerImpl(null, SMTPS, noneOf(EmailAddressCriteria.class), proxyBypassingMock, session, createDummyOperationalConfig(EMPTY_LIST, true, false)); + new MailerImpl(null, SMTPS, createDummyEmailGovernance(), proxyBypassingMock, session, createDummyOperationalConfig(EMPTY_LIST, true, false)); assertThat(session.getProperties().getProperty("mail.smtps.ssl.trust")).isEqualTo("*"); assertThat(session.getProperties().getProperty("mail.smtp.ssl.checkserveridentity")).isNull(); assertThat(session.getProperties().getProperty("mail.smtps.ssl.checkserveridentity")).isEqualTo("false"); - new MailerImpl(null, SMTPS, noneOf(EmailAddressCriteria.class), proxyBypassingMock, session, createDummyOperationalConfig(EMPTY_LIST, false, true)); + new MailerImpl(null, SMTPS, createDummyEmailGovernance(), proxyBypassingMock, session, createDummyOperationalConfig(EMPTY_LIST, false, true)); assertThat(session.getProperties().getProperty("mail.smtps.ssl.trust")).isNull(); assertThat(session.getProperties().getProperty("mail.smtp.ssl.checkserveridentity")).isNull(); assertThat(session.getProperties().getProperty("mail.smtps.ssl.checkserveridentity")).isEqualTo("true"); @@ -69,11 +63,11 @@ public void trustAllHosts_SMTPS() { @Test public void trustAllHosts_SMTP_TLS() { - new MailerImpl(null, SMTP_TLS, noneOf(EmailAddressCriteria.class), createEmptyProxyConfig(), session, createDummyOperationalConfig(EMPTY_LIST, true, false)); + new MailerImpl(null, SMTP_TLS, createDummyEmailGovernance(), createEmptyProxyConfig(), session, createDummyOperationalConfig(EMPTY_LIST, true, false)); assertThat(session.getProperties().getProperty("mail.smtp.ssl.trust")).isEqualTo("*"); assertThat(session.getProperties().getProperty("mail.smtp.ssl.checkserveridentity")).isEqualTo("false"); assertThat(session.getProperties().getProperty("mail.smtps.ssl.checkserveridentity")).isNull(); - new MailerImpl(null, SMTP_TLS, noneOf(EmailAddressCriteria.class), createEmptyProxyConfig(), session, createDummyOperationalConfig(EMPTY_LIST, false, true)); + new MailerImpl(null, SMTP_TLS, createDummyEmailGovernance(), createEmptyProxyConfig(), session, createDummyOperationalConfig(EMPTY_LIST, false, true)); assertThat(session.getProperties().getProperty("mail.smtps.ssl.trust")).isNull(); assertThat(session.getProperties().getProperty("mail.smtp.ssl.checkserveridentity")).isEqualTo("true"); assertThat(session.getProperties().getProperty("mail.smtps.ssl.checkserveridentity")).isNull(); @@ -81,26 +75,48 @@ public void trustAllHosts_SMTP_TLS() { @Test public void trustHosts() { - new MailerImpl(null, SMTP, noneOf(EmailAddressCriteria.class), createEmptyProxyConfig(), session, createDummyOperationalConfig(asList(), false, false)); + new MailerImpl(null, SMTP, createDummyEmailGovernance(), createEmptyProxyConfig(), session, createDummyOperationalConfig(asList(), false, false)); assertThat(session.getProperties().getProperty("mail.smtp.ssl.trust")).isNull(); assertThat(session.getProperties().getProperty("mail.smtp.ssl.checkserveridentity")).isNull(); assertThat(session.getProperties().getProperty("mail.smtps.ssl.checkserveridentity")).isNull(); - new MailerImpl(null, SMTP, noneOf(EmailAddressCriteria.class), createEmptyProxyConfig(), session, createDummyOperationalConfig(asList("a"), false, false)); + new MailerImpl(null, SMTP, createDummyEmailGovernance(), createEmptyProxyConfig(), session, createDummyOperationalConfig(asList("a"), false, false)); assertThat(session.getProperties().getProperty("mail.smtp.ssl.trust")).isEqualTo("a"); assertThat(session.getProperties().getProperty("mail.smtp.ssl.checkserveridentity")).isNull(); assertThat(session.getProperties().getProperty("mail.smtps.ssl.checkserveridentity")).isNull(); - new MailerImpl(null, SMTP, noneOf(EmailAddressCriteria.class), createEmptyProxyConfig(), session, createDummyOperationalConfig(asList("a", "b"), false, false)); + new MailerImpl(null, SMTP, createDummyEmailGovernance(), createEmptyProxyConfig(), session, createDummyOperationalConfig(asList("a", "b"), false, false)); assertThat(session.getProperties().getProperty("mail.smtp.ssl.trust")).isEqualTo("a b"); assertThat(session.getProperties().getProperty("mail.smtp.ssl.checkserveridentity")).isNull(); assertThat(session.getProperties().getProperty("mail.smtps.ssl.checkserveridentity")).isNull(); - new MailerImpl(null, SMTP, noneOf(EmailAddressCriteria.class), createEmptyProxyConfig(), session, createDummyOperationalConfig(asList("a", "b", "c"), false, true)); + new MailerImpl(null, SMTP, createDummyEmailGovernance(), createEmptyProxyConfig(), session, createDummyOperationalConfig(asList("a", "b", "c"), false, true)); assertThat(session.getProperties().getProperty("mail.smtp.ssl.trust")).isEqualTo("a b c"); assertThat(session.getProperties().getProperty("mail.smtp.ssl.checkserveridentity")).isNull(); assertThat(session.getProperties().getProperty("mail.smtps.ssl.checkserveridentity")).isNull(); } - + + @Test + public void testSignWithSmime_WithConfigObject() { + final EmailGovernanceImpl emailGovernance = new EmailGovernanceImpl(noneOf(EmailAddressCriteria.class), loadPkcs12KeyStore()); + final Mailer mailer = new MailerImpl(null, SMTP, emailGovernance, createEmptyProxyConfig(), session, createDummyOperationalConfig(EMPTY_LIST, true, false)); + + assertThat(mailer.getEmailGovernance().getPkcs12ConfigForSmimeSigning()).isNotNull(); + assertThat(mailer.getEmailGovernance().getPkcs12ConfigForSmimeSigning().getPkcs12StoreData()).isNotNull(); + assertThat(mailer.getEmailGovernance().getPkcs12ConfigForSmimeSigning().getStorePassword()).isEqualTo("letmein".toCharArray()); + assertThat(mailer.getEmailGovernance().getPkcs12ConfigForSmimeSigning().getKeyAlias()).isEqualTo("smime_test_user_alias"); + assertThat(mailer.getEmailGovernance().getPkcs12ConfigForSmimeSigning().getKeyPassword()).isEqualTo("letmein".toCharArray()); + } + @NotNull private List asList(String... args) { return Arrays.asList(args); } + + @NotNull + private ProxyConfig createEmptyProxyConfig() { + return new ProxyConfigImpl(null, null, null, null, null); + } + + @NotNull + private EmailGovernance createDummyEmailGovernance() { + return new EmailGovernanceImpl(noneOf(EmailAddressCriteria.class), null); + } } \ No newline at end of file diff --git a/modules/smime-module/src/main/java/org/simplejavamail/internal/smimesupport/SMIMESupport.java b/modules/smime-module/src/main/java/org/simplejavamail/internal/smimesupport/SMIMESupport.java index b41e6c15f..399690841 100644 --- a/modules/smime-module/src/main/java/org/simplejavamail/internal/smimesupport/SMIMESupport.java +++ b/modules/smime-module/src/main/java/org/simplejavamail/internal/smimesupport/SMIMESupport.java @@ -36,6 +36,7 @@ import org.simplejavamail.internal.smimesupport.builder.SmimeParseResultBuilder; import org.simplejavamail.internal.smimesupport.model.OriginalSmimeDetailsImpl; import org.simplejavamail.internal.smimesupport.model.SmimeDetailsImpl; +import org.simplejavamail.internal.util.SimpleOptional; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -351,6 +352,7 @@ public String getSignedByAddress(@NotNull AttachmentResource smimeAttachment) { throw new SmimeException(format(ERROR_EXTRACTING_SIGNEDBY_FROM_SMIME_SIGNED_ATTACHMENT, smimeAttachment), e); } } + /** * Delegates to {@link #determineSMIMESigned(MimePart)} and {@link #getSignedByAddress(SMIMESigned)}. * @@ -434,10 +436,15 @@ private static SignerInformationVerifier getVerifier(X509Certificate certificate @NotNull @Override - public MimeMessage signAndOrEncryptEmail(@Nullable final Session session, @NotNull final MimeMessage messageToProtect, @NotNull final Email emailContainingSmimeDetails) { + public MimeMessage signAndOrEncryptEmail(@Nullable final Session session, @NotNull final MimeMessage messageToProtect, @NotNull final Email emailContainingSmimeDetails, + @Nullable final Pkcs12Config defaultSmimeSigningStore) { MimeMessage result = messageToProtect; - if (emailContainingSmimeDetails.getPkcs12ConfigForSmimeSigning() != null) { - result = signMessage(session, result, emailContainingSmimeDetails.getPkcs12ConfigForSmimeSigning()); + + final Pkcs12Config pkcs12Config = SimpleOptional + .ofNullable(emailContainingSmimeDetails.getPkcs12ConfigForSmimeSigning()) + .orMaybe(defaultSmimeSigningStore); + if (pkcs12Config != null) { + result = signMessage(session, result, pkcs12Config); } if (emailContainingSmimeDetails.getX509CertificateForSmimeEncryption() != null) { result = encryptMessage(session, result, emailContainingSmimeDetails.getX509CertificateForSmimeEncryption()); diff --git a/modules/spring-module/src/test/resources/pkcs12/smime_keystore.pkcs12 b/modules/spring-module/src/test/resources/pkcs12/smime_keystore.pkcs12 new file mode 100644 index 000000000..621868f34 Binary files /dev/null and b/modules/spring-module/src/test/resources/pkcs12/smime_keystore.pkcs12 differ