Skip to content

Commit 860e437

Browse files
committed
#461: Preserve fixed MessageID for DKIM and S/MIME sgned/encrypted messages
1 parent d8330d3 commit 860e437

File tree

12 files changed

+117
-43
lines changed

12 files changed

+117
-43
lines changed

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import jakarta.mail.internet.MimeMessage;
44
import org.jetbrains.annotations.NotNull;
5+
import org.simplejavamail.api.email.Email;
56
import org.simplejavamail.api.email.Recipient;
67
import org.simplejavamail.api.email.config.DkimConfig;
78

@@ -17,10 +18,11 @@ public interface DKIMModule {
1718
* {@code org.simplejavamail.utils.mail.dkim.DkimMessage} and {@code org.simplejavamail.utils.mail.dkim.DkimSigner}
1819
* during the physical sending of the message.
1920
*
21+
* @param email The Email from which the MimeMessage was produced. Used to take fixed Message-ID from, if applicable.
2022
* @param messageToSign The message to be signed when sent.
2123
* @param dkimConfig The {@link DkimConfig} containing all DKIM signing details
2224
* @param fromRecipient The "From" recipient to be used as identity
2325
* @return The original mime message wrapped in a new one that performs signing when sent.
2426
*/
25-
MimeMessage signMessageWithDKIM(@NotNull MimeMessage messageToSign, @NotNull DkimConfig dkimConfig, @NotNull Recipient fromRecipient);
27+
MimeMessage signMessageWithDKIM(@NotNull Email email, @NotNull MimeMessage messageToSign, @NotNull DkimConfig dkimConfig, @NotNull Recipient fromRecipient);
2628
}

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,8 +70,8 @@ public interface SMIMEModule {
7070
boolean verifyValidSignature(@NotNull MimeMessage mimeMessage, @NotNull OriginalSmimeDetails messageSmimeDetails);
7171

7272
@NotNull
73-
MimeMessage signMessageWithSmime(@NotNull Session session, @NotNull MimeMessage messageToProtect, @NotNull Pkcs12Config pkcs12Config);
73+
MimeMessage signMessageWithSmime(@NotNull Session session, @NotNull final Email email, @NotNull MimeMessage messageToProtect, @NotNull Pkcs12Config pkcs12Config);
7474

7575
@NotNull
76-
MimeMessage encryptMessageWithSmime(@NotNull Session session, @NotNull MimeMessage messageToProtect, @NotNull X509Certificate x509Certificate);
76+
MimeMessage encryptMessageWithSmime(@NotNull Session session, @NotNull final Email email, @NotNull MimeMessage messageToProtect, @NotNull X509Certificate x509Certificate);
7777
}

modules/dkim-module/src/main/java/org/simplejavamail/internal/dkimsupport/DKIMSigner.java

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import jakarta.mail.MessagingException;
44
import jakarta.mail.internet.MimeMessage;
55
import org.jetbrains.annotations.NotNull;
6+
import org.simplejavamail.api.email.Email;
67
import org.simplejavamail.api.email.Recipient;
78
import org.simplejavamail.api.email.config.DkimConfig;
89
import org.simplejavamail.internal.modules.DKIMModule;
@@ -30,9 +31,9 @@ public class DKIMSigner implements DKIMModule {
3031
private static final Logger LOGGER = LoggerFactory.getLogger(DKIMSigner.class);
3132

3233
/**
33-
* @see DKIMModule#signMessageWithDKIM(MimeMessage, DkimConfig, Recipient)
34+
* @see DKIMModule#signMessageWithDKIM(Email email, MimeMessage, DkimConfig, Recipient)
3435
*/
35-
public MimeMessage signMessageWithDKIM(@NotNull final MimeMessage messageToSign, @NotNull final DkimConfig dkimConfig, @NotNull final Recipient fromRecipient) {
36+
public MimeMessage signMessageWithDKIM(@NotNull Email email, @NotNull final MimeMessage messageToSign, @NotNull final DkimConfig dkimConfig, @NotNull final Recipient fromRecipient) {
3637
LOGGER.debug("signing MimeMessage with DKIM...");
3738
try {
3839
final DkimSigner dkimSigner = new DkimSigner(dkimConfig.getDkimSigningDomain(), dkimConfig.getDkimSelector(), new ByteArrayInputStream(dkimConfig.getDkimPrivateKeyData()));
@@ -51,7 +52,7 @@ public MimeMessage signMessageWithDKIM(@NotNull final MimeMessage messageToSign,
5152
dkimSigner.setCheckDomainKey(false);
5253
}
5354

54-
return new DkimMessage(messageToSign, dkimSigner);
55+
return new DkimMessageIdFixingMimeMessage(messageToSign, dkimSigner, email.getId());
5556
} catch (IOException | NoSuchAlgorithmException | InvalidKeySpecException | MessagingException e) {
5657
throw new org.simplejavamail.internal.dkimsupport.DKIMSigningException(org.simplejavamail.internal.dkimsupport.DKIMSigningException.ERROR_SIGNING_DKIM_INVALID_DOMAINKEY, e);
5758
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
package org.simplejavamail.internal.dkimsupport;
2+
3+
import jakarta.mail.MessagingException;
4+
import jakarta.mail.internet.MimeMessage;
5+
import org.jetbrains.annotations.Nullable;
6+
import org.simplejavamail.utils.mail.dkim.DkimMessage;
7+
import org.simplejavamail.utils.mail.dkim.DkimSigner;
8+
9+
import static java.lang.String.format;
10+
import static org.simplejavamail.internal.util.MiscUtil.valueNullOrEmpty;
11+
12+
public class DkimMessageIdFixingMimeMessage extends DkimMessage {
13+
@Nullable
14+
private final String messageId;
15+
16+
public DkimMessageIdFixingMimeMessage(MimeMessage message, DkimSigner signer, @Nullable String messageId) throws MessagingException {
17+
super(message, signer);
18+
this.messageId = messageId;
19+
}
20+
21+
@Override
22+
protected void updateMessageID() throws MessagingException {
23+
if (valueNullOrEmpty(messageId)) {
24+
super.updateMessageID();
25+
} else {
26+
setHeader("Message-ID", messageId);
27+
}
28+
}
29+
30+
@Override
31+
public String toString() {
32+
try {
33+
return format("DkimMessage<id:%s, subject:%s>", super.getMessageID(), super.getSubject());
34+
} catch (MessagingException e) {
35+
throw new IllegalStateException("should not reach here");
36+
}
37+
}
38+
}

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

Lines changed: 5 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,13 @@
66
import org.jetbrains.annotations.NotNull;
77
import org.simplejavamail.api.email.Email;
88
import org.simplejavamail.internal.moduleloader.ModuleLoader;
9+
import org.simplejavamail.mailer.internal.util.MessageIdFixingMimeMessage;
910

1011
import java.io.UnsupportedEncodingException;
1112
import java.util.Date;
1213

13-
import static java.lang.String.format;
1414
import static java.util.Optional.ofNullable;
1515
import static org.simplejavamail.internal.util.MiscUtil.checkArgumentNotEmpty;
16-
import static org.simplejavamail.internal.util.MiscUtil.valueNullOrEmpty;
1716
import static org.simplejavamail.internal.util.Preconditions.checkNonEmptyArgument;
1817

1918
/**
@@ -43,25 +42,7 @@ final MimeMessage populateMimeMessage(@NotNull final Email email, @NotNull Sessi
4342
checkArgumentNotEmpty(email, "email is missing");
4443
checkArgumentNotEmpty(session, "session is needed, it cannot be attached later");
4544

46-
MimeMessage message = new MimeMessage(session) {
47-
@Override
48-
protected void updateMessageID() throws MessagingException {
49-
if (valueNullOrEmpty(email.getId())) {
50-
super.updateMessageID();
51-
} else {
52-
setHeader("Message-ID", email.getId());
53-
}
54-
}
55-
56-
@Override
57-
public String toString() {
58-
try {
59-
return format("MimeMessage<id:%s, subject:%s>", super.getMessageID(), super.getSubject());
60-
} catch (MessagingException e) {
61-
throw new IllegalStateException("should not reach here");
62-
}
63-
}
64-
};
45+
MimeMessage message = new MessageIdFixingMimeMessage(session, email.getId());
6546

6647
// set basic email properties
6748
MimeMessageHelper.setSubject(email, message);
@@ -82,15 +63,15 @@ public String toString() {
8263
*/
8364

8465
if (email.getPkcs12ConfigForSmimeSigning() != null) {
85-
message = ModuleLoader.loadSmimeModule().signMessageWithSmime(session, message, email.getPkcs12ConfigForSmimeSigning());
66+
message = ModuleLoader.loadSmimeModule().signMessageWithSmime(session, email, message, email.getPkcs12ConfigForSmimeSigning());
8667
}
8768

8869
if (email.getX509CertificateForSmimeEncryption() != null) {
89-
message = ModuleLoader.loadSmimeModule().encryptMessageWithSmime(session, message, email.getX509CertificateForSmimeEncryption());
70+
message = ModuleLoader.loadSmimeModule().encryptMessageWithSmime(session, email, message, email.getX509CertificateForSmimeEncryption());
9071
}
9172

9273
if (email.getDkimConfig() != null) {
93-
message = ModuleLoader.loadDKIMModule().signMessageWithDKIM(message, email.getDkimConfig(), checkNonEmptyArgument(email.getFromRecipient(), "fromRecipient"));
74+
message = ModuleLoader.loadDKIMModule().signMessageWithDKIM(email, message, email.getDkimConfig(), checkNonEmptyArgument(email.getFromRecipient(), "fromRecipient"));
9475
}
9576

9677
if (email.getBounceToRecipient() != null) {

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

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -241,26 +241,26 @@ public static void scanForInjectionAttack(final @Nullable String value, final St
241241
}
242242

243243
/**
244-
* @see org.simplejavamail.internal.modules.DKIMModule#signMessageWithDKIM(MimeMessage, DkimConfig, Recipient)
244+
* @see org.simplejavamail.internal.modules.DKIMModule#signMessageWithDKIM(Email, MimeMessage, DkimConfig, Recipient)
245245
*/
246246
@SuppressWarnings("unused")
247247
public static MimeMessage signMessageWithDKIM(@NotNull final MimeMessage messageToSign, @NotNull final Email emailContainingSigningDetails) {
248248
val dkimConfig = requireNonNull(emailContainingSigningDetails.getDkimConfig(), "email.dkimConfig");
249249
val fromRecipient = requireNonNull(emailContainingSigningDetails.getFromRecipient(), "email.fromRecipient");
250-
return ModuleLoader.loadDKIMModule().signMessageWithDKIM(messageToSign, dkimConfig, fromRecipient);
250+
return ModuleLoader.loadDKIMModule().signMessageWithDKIM(emailContainingSigningDetails, messageToSign, dkimConfig, fromRecipient);
251251
}
252252

253253
/**
254254
* Depending on the Email configuration, signs and then encrypts message (both steps optional), using the S/MIME module.
255255
*
256-
* @see org.simplejavamail.internal.modules.SMIMEModule#signMessageWithSmime(Session, MimeMessage, Pkcs12Config)
257-
* @see org.simplejavamail.internal.modules.SMIMEModule#encryptMessageWithSmime(Session, MimeMessage, X509Certificate)
256+
* @see org.simplejavamail.internal.modules.SMIMEModule#signMessageWithSmime(Session, Email, MimeMessage, Pkcs12Config)
257+
* @see org.simplejavamail.internal.modules.SMIMEModule#encryptMessageWithSmime(Session, Email, MimeMessage, X509Certificate)
258258
*/
259259
@SuppressWarnings("unused")
260260
public static MimeMessage signAndOrEncryptMessageWithSmime(@NotNull final Session session, @NotNull final MimeMessage messageToProtect, @NotNull final Email emailContainingSmimeDetails) {
261261
MimeMessage message = messageToProtect;
262-
message = ModuleLoader.loadSmimeModule().signMessageWithSmime(session, message, requireNonNull(emailContainingSmimeDetails.getPkcs12ConfigForSmimeSigning(), "Pkcs12Config"));
263-
message = ModuleLoader.loadSmimeModule().encryptMessageWithSmime(session, message, requireNonNull(emailContainingSmimeDetails.getX509CertificateForSmimeEncryption(), "X509Certificate"));
262+
message = ModuleLoader.loadSmimeModule().signMessageWithSmime(session, emailContainingSmimeDetails, message, requireNonNull(emailContainingSmimeDetails.getPkcs12ConfigForSmimeSigning(), "Pkcs12Config"));
263+
message = ModuleLoader.loadSmimeModule().encryptMessageWithSmime(session, emailContainingSmimeDetails, message, requireNonNull(emailContainingSmimeDetails.getX509CertificateForSmimeEncryption(), "X509Certificate"));
264264
return message;
265265
}
266266
}

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

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,14 @@
1212
import org.simplejavamail.api.mailer.EmailTooBigException;
1313
import org.simplejavamail.api.mailer.config.EmailGovernance;
1414
import org.simplejavamail.api.mailer.config.OperationalConfig;
15+
import org.simplejavamail.converter.internal.mimemessage.ImmutableDelegatingSMTPMessage;
1516
import org.simplejavamail.converter.internal.mimemessage.MimeMessageProducerHelper;
1617
import org.simplejavamail.email.internal.InternalEmail;
18+
import org.simplejavamail.internal.dkimsupport.DkimMessageIdFixingMimeMessage;
19+
import org.simplejavamail.mailer.internal.util.MessageIdFixingMimeMessage;
1720
import org.simplejavamail.mailer.internal.util.SessionLogger;
21+
import org.simplejavamail.utils.mail.smime.SmimeMessageIdFixingMimeMessage;
22+
import org.simplejavamail.utils.mail.smime.SmimeMessageIdFixingSMTPMessage;
1823
import org.slf4j.Logger;
1924
import org.slf4j.LoggerFactory;
2025

@@ -84,6 +89,15 @@ private MimeMessage convertAndLogMimeMessage(final Email email) throws Messaging
8489
val message = convertMimeMessage(email, session);
8590

8691
SessionLogger.logSession(session, operationalConfig.isAsync(), "mail");
92+
93+
if (!(message instanceof MessageIdFixingMimeMessage) &&
94+
!(message instanceof DkimMessageIdFixingMimeMessage) &&
95+
!(message instanceof ImmutableDelegatingSMTPMessage) &&
96+
!(message instanceof SmimeMessageIdFixingMimeMessage) &&
97+
!(message instanceof SmimeMessageIdFixingSMTPMessage)) {
98+
throw new AssertionError("Wrong MimeMessage type; will be unable to fix Message-ID on message.saveChanges()");
99+
}
100+
87101
message.saveChanges(); // some headers and id's will be set for this specific message
88102

89103
//noinspection deprecation
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
package org.simplejavamail.mailer.internal.util;
2+
3+
import jakarta.mail.MessagingException;
4+
import jakarta.mail.Session;
5+
import jakarta.mail.internet.MimeMessage;
6+
import org.jetbrains.annotations.Nullable;
7+
8+
import static java.lang.String.format;
9+
import static org.simplejavamail.internal.util.MiscUtil.valueNullOrEmpty;
10+
11+
public class MessageIdFixingMimeMessage extends MimeMessage {
12+
@Nullable
13+
private final String messageId;
14+
15+
public MessageIdFixingMimeMessage(Session session, @Nullable String messageId) {
16+
super(session);
17+
this.messageId = messageId;
18+
}
19+
20+
@Override
21+
protected void updateMessageID() throws MessagingException {
22+
if (valueNullOrEmpty(messageId)) {
23+
super.updateMessageID();
24+
} else {
25+
setHeader("Message-ID", messageId);
26+
}
27+
}
28+
29+
@Override
30+
public String toString() {
31+
try {
32+
return format("MimeMessage<id:%s, subject:%s>", super.getMessageID(), super.getSubject());
33+
} catch (MessagingException e) {
34+
throw new IllegalStateException("should not reach here");
35+
}
36+
}
37+
}

modules/simple-java-mail/src/test/java/org/simplejavamail/mailer/MailerLiveTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -289,7 +289,7 @@ public void createMailSession_OutlookMessageSmimeSignEncryptTest()
289289

290290
@Test
291291
public void testEncryptSendAndReceiveDecrypt()
292-
throws IOException, MessagingException, ExecutionException, InterruptedException {
292+
throws MessagingException, ExecutionException, InterruptedException {
293293
val builder = EmailHelper.createDummyEmailBuilder(null, true, true, true, false, false, false, false)
294294
.encryptWithSmime(new File(RESOURCES_PKCS + "/smime_test_user.pem.standard.crt"));
295295

modules/smime-module/pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@
3434
<dependency>
3535
<groupId>org.simplejavamail</groupId>
3636
<artifactId>utils-mail-smime</artifactId>
37-
<version>2.1.0</version>
37+
<version>2.1.1</version>
3838
</dependency>
3939
</dependencies>
4040
</project>

0 commit comments

Comments
 (0)