Minor optimizations

pull/134/head
Richard Körber 2022-11-25 13:14:33 +01:00
parent 06e03880c9
commit ef5ca28fd9
No known key found for this signature in database
GPG Key ID: AAB9FD19C78AA3E0
3 changed files with 81 additions and 63 deletions

View File

@ -135,13 +135,16 @@ public final class EmailProcessor {
boolean hasMatch = false;
for (SignerInformation signer : signed.getSignerInfos().getSigners()) {
hasMatch |= signer.verify(verifier);
if (hasMatch) {
break;
}
}
if (!hasMatch) {
throw new AcmeInvalidMessageException("The S/MIME signature is invalid");
}
MimeMessage content = signed.getContentAsMimeMessage(mailSession);
if (!content.getContentType().equalsIgnoreCase("message/rfc822; forwarded=no")) {
if (!"message/rfc822; forwarded=no".equalsIgnoreCase(content.getContentType())) {
throw new AcmeInvalidMessageException("Message does not contain protected headers");
}
@ -214,7 +217,7 @@ public final class EmailProcessor {
if (from == null) {
throw new AcmeInvalidMessageException("Message has no 'From' header");
}
if (from.length != 1) {
if (from.length != 1 || from[0] == null) {
throw new AcmeInvalidMessageException("Message must have exactly one sender, but has " + from.length);
}
if (validFromAddresses != null && !validFromAddresses.contains(from[0])) {
@ -222,8 +225,7 @@ public final class EmailProcessor {
}
if (strict && signedMessage != null) {
Address[] outerFrom = message.getFrom();
if ((outerFrom.length > 1) || (outerFrom.length == 1 && outerFrom[0] != null
&& !outerFrom[0].equals(from[0]))) {
if (outerFrom == null || outerFrom.length != 1 || !from[0].equals(outerFrom[0])) {
throw new AcmeInvalidMessageException("Protected 'From' header does not match envelope header");
}
}
@ -233,13 +235,12 @@ public final class EmailProcessor {
if (to == null) {
throw new AcmeInvalidMessageException("Message has no 'To' header");
}
if (to.length != 1) {
if (to.length != 1 || to[0] == null) {
throw new AcmeProtocolException("Message must have exactly one recipient, but has " + to.length);
}
if (strict && signedMessage != null) {
Address[] outerTo = message.getRecipients(TO);
if ((outerTo.length > 1) || (outerTo.length == 1 && outerTo[0] != null
&& !outerTo[0].equals(to[0]))) {
if (outerTo == null || outerTo.length != 1 || !to[0].equals(outerTo[0])) {
throw new AcmeInvalidMessageException("Protected 'To' header does not match envelope header");
}
}
@ -249,9 +250,8 @@ public final class EmailProcessor {
if (subject == null) {
throw new AcmeInvalidMessageException("Message has no 'Subject' header");
}
if (strict && signedMessage != null
&& message.getSubject() != null
&& !message.getSubject().equals(signedMessage.getSubject())) {
if (strict && signedMessage != null &&
(message.getSubject() == null || !message.getSubject().equals(signedMessage.getSubject()))) {
throw new AcmeInvalidMessageException("Protected 'Subject' header does not match envelope header");
}
Matcher m = SUBJECT_PATTERN.matcher(subject);

View File

@ -68,9 +68,9 @@ public abstract class SMIMETests {
*
* @param name
* Name of the mock message to be read from the test resources.
* @return Mock {@link Message} that was created
* @return Mock {@link MimeMessage} that was created
*/
protected Message mockMessage(String name) {
protected MimeMessage mockMessage(String name) {
try (InputStream in = SMIMETests.class.getResourceAsStream("/email/" + name + ".eml")) {
return new MimeMessage(mailSession, in);
} catch (IOException ex) {

View File

@ -67,19 +67,21 @@ public class EmailProcessorTest extends SMIMETests {
}
@Test
public void testValidSignature() throws AcmeInvalidMessageException, IOException {
MimeMessage message = (MimeMessage) mockMessage("valid-mail");
X509Certificate certificate = readCertificate("valid-signer");
EmailProcessor processor = EmailProcessor.smimeMessage(message, mailSession, certificate, true);
public void testValidSignature() {
assertThatNoException().isThrownBy(() -> {
MimeMessage message = mockMessage("valid-mail");
X509Certificate certificate = readCertificate("valid-signer");
EmailProcessor.smimeMessage(message, mailSession, certificate, true);
});
}
@Test
public void testInvalidSignature() {
assertThatExceptionOfType(AcmeInvalidMessageException.class)
.isThrownBy(() -> {
MimeMessage message = (MimeMessage) mockMessage("invalid-signed-mail");
MimeMessage message = mockMessage("invalid-signed-mail");
X509Certificate certificate = readCertificate("valid-signer");
EmailProcessor processor = EmailProcessor.smimeMessage(message, mailSession, certificate, true);
EmailProcessor.smimeMessage(message, mailSession, certificate, true);
})
.withMessage("The S/MIME signature is invalid");
}
@ -88,9 +90,9 @@ public class EmailProcessorTest extends SMIMETests {
public void testValidSignatureButNoSAN() {
assertThatExceptionOfType(AcmeInvalidMessageException.class)
.isThrownBy(() -> {
MimeMessage message = (MimeMessage) mockMessage("invalid-nosan");
MimeMessage message = mockMessage("invalid-nosan");
X509Certificate certificate = readCertificate("valid-signer-nosan");
EmailProcessor processor = EmailProcessor.smimeMessage(message, mailSession, certificate, true);
EmailProcessor.smimeMessage(message, mailSession, certificate, true);
})
.withMessage("Signing certificate does not provide a rfc822Name subjectAltName");
}
@ -99,9 +101,9 @@ public class EmailProcessorTest extends SMIMETests {
public void testSANDoesNotMatchFrom() {
assertThatExceptionOfType(AcmeInvalidMessageException.class)
.isThrownBy(() -> {
MimeMessage message = (MimeMessage) mockMessage("invalid-cert-mismatch");
MimeMessage message = mockMessage("invalid-cert-mismatch");
X509Certificate certificate = readCertificate("valid-signer");
EmailProcessor processor = EmailProcessor.smimeMessage(message, mailSession, certificate, true);
EmailProcessor.smimeMessage(message, mailSession, certificate, true);
})
.withMessage("Sender 'different-ca@example.com' was not found in signing certificate");
}
@ -110,9 +112,9 @@ public class EmailProcessorTest extends SMIMETests {
public void testInvalidProtectedFromHeader() {
assertThatExceptionOfType(AcmeInvalidMessageException.class)
.isThrownBy(() -> {
MimeMessage message = (MimeMessage) mockMessage("invalid-protected-mail-from");
MimeMessage message = mockMessage("invalid-protected-mail-from");
X509Certificate certificate = readCertificate("valid-signer");
EmailProcessor processor = EmailProcessor.smimeMessage(message, mailSession, certificate, true);
EmailProcessor.smimeMessage(message, mailSession, certificate, true);
})
.withMessage("Protected 'From' header does not match envelope header");
}
@ -121,9 +123,9 @@ public class EmailProcessorTest extends SMIMETests {
public void testInvalidProtectedToHeader() {
assertThatExceptionOfType(AcmeInvalidMessageException.class)
.isThrownBy(() -> {
MimeMessage message = (MimeMessage) mockMessage("invalid-protected-mail-to");
MimeMessage message = mockMessage("invalid-protected-mail-to");
X509Certificate certificate = readCertificate("valid-signer");
EmailProcessor processor = EmailProcessor.smimeMessage(message, mailSession, certificate, true);
EmailProcessor.smimeMessage(message, mailSession, certificate, true);
})
.withMessage("Protected 'To' header does not match envelope header");
}
@ -132,9 +134,9 @@ public class EmailProcessorTest extends SMIMETests {
public void testInvalidProtectedSubjectHeader() {
assertThatExceptionOfType(AcmeInvalidMessageException.class)
.isThrownBy(() -> {
MimeMessage message = (MimeMessage) mockMessage("invalid-protected-mail-subject");
MimeMessage message = mockMessage("invalid-protected-mail-subject");
X509Certificate certificate = readCertificate("valid-signer");
EmailProcessor processor = EmailProcessor.smimeMessage(message, mailSession, certificate, true);
EmailProcessor.smimeMessage(message, mailSession, certificate, true);
})
.withMessage("Protected 'Subject' header does not match envelope header");
}
@ -143,66 +145,80 @@ public class EmailProcessorTest extends SMIMETests {
public void testNonStrictInvalidProtectedSubjectHeader() {
assertThatNoException()
.isThrownBy(() -> {
MimeMessage message = (MimeMessage) mockMessage("invalid-protected-mail-subject");
MimeMessage message = mockMessage("invalid-protected-mail-subject");
X509Certificate certificate = readCertificate("valid-signer");
EmailProcessor processor = EmailProcessor.smimeMessage(message, mailSession, certificate, false);
EmailProcessor.smimeMessage(message, mailSession, certificate, false);
});
}
@Test
public void textExpectedFromFails() {
assertThrows(AcmeProtocolException.class, () -> {
EmailProcessor processor = EmailProcessor.plainMessage(message);
processor.expectedFrom(expectedTo);
});
assertThatExceptionOfType(AcmeProtocolException.class)
.isThrownBy(() -> {
EmailProcessor processor = EmailProcessor.plainMessage(message);
processor.expectedFrom(expectedTo);
})
.withMessage("Message is not sent by the expected sender");
}
@Test
public void textExpectedToFails() {
assertThrows(AcmeProtocolException.class, () -> {
EmailProcessor processor = EmailProcessor.plainMessage(message);
processor.expectedTo(expectedFrom);
});
assertThatExceptionOfType(AcmeProtocolException.class)
.isThrownBy(() -> {
EmailProcessor processor = EmailProcessor.plainMessage(message);
processor.expectedTo(expectedFrom);
})
.withMessage("Message is not addressed to expected recipient");
}
@Test
public void textExpectedIdentifierFails1() {
assertThrows(AcmeProtocolException.class, () -> {
EmailProcessor processor = EmailProcessor.plainMessage(message);
processor.expectedIdentifier(EmailIdentifier.email(expectedFrom));
});
assertThatExceptionOfType(AcmeProtocolException.class)
.isThrownBy(() -> {
EmailProcessor processor = EmailProcessor.plainMessage(message);
processor.expectedIdentifier(EmailIdentifier.email(expectedFrom));
})
.withMessage("Message is not addressed to expected recipient");
}
@Test
public void textExpectedIdentifierFails2() {
assertThrows(AcmeProtocolException.class, () -> {
EmailProcessor processor = EmailProcessor.plainMessage(message);
processor.expectedIdentifier(Identifier.ip("192.168.0.1"));
});
assertThatExceptionOfType(AcmeProtocolException.class)
.isThrownBy(() -> {
EmailProcessor processor = EmailProcessor.plainMessage(message);
processor.expectedIdentifier(Identifier.ip("192.168.0.1"));
})
.withMessage("Wrong identifier type: ip");
}
@Test
public void textNoChallengeFails1() {
assertThrows(IllegalStateException.class, () -> {
EmailProcessor processor = EmailProcessor.plainMessage(message);
processor.getToken();
});
assertThatExceptionOfType(IllegalStateException.class)
.isThrownBy(() -> {
EmailProcessor processor = EmailProcessor.plainMessage(message);
processor.getToken();
})
.withMessage("No challenge has been set yet");
}
@Test
public void textNoChallengeFails2() {
assertThrows(IllegalStateException.class, () -> {
EmailProcessor processor = EmailProcessor.plainMessage(message);
processor.getAuthorization();
});
assertThatExceptionOfType(IllegalStateException.class)
.isThrownBy(() -> {
EmailProcessor processor = EmailProcessor.plainMessage(message);
processor.getAuthorization();
})
.withMessage("No challenge has been set yet");
}
@Test
public void textNoChallengeFails3() {
assertThrows(IllegalStateException.class, () -> {
EmailProcessor processor = EmailProcessor.plainMessage(message);
processor.respond();
});
assertThatExceptionOfType(IllegalStateException.class)
.isThrownBy(() -> {
EmailProcessor processor = EmailProcessor.plainMessage(message);
processor.respond();
})
.withMessage("No challenge has been set yet");
}
@Test
@ -218,11 +234,13 @@ public class EmailProcessorTest extends SMIMETests {
@Test
public void testChallengeMismatch() {
assertThrows(AcmeProtocolException.class, () -> {
EmailReply00Challenge challenge = mockChallenge("emailReplyChallengeMismatch");
EmailProcessor processor = EmailProcessor.plainMessage(message);
processor.withChallenge(challenge);
});
assertThatExceptionOfType(AcmeProtocolException.class)
.isThrownBy(() -> {
EmailReply00Challenge challenge = mockChallenge("emailReplyChallengeMismatch");
EmailProcessor processor = EmailProcessor.plainMessage(message);
processor.withChallenge(challenge);
})
.withMessage("Message is not sent by the expected sender");
}
@Test