mirror of https://github.com/shred/acme4j
parent
aae98d7ce8
commit
db8eb4d012
|
@ -21,14 +21,12 @@ import java.security.KeyStore;
|
|||
import java.security.KeyStoreException;
|
||||
import java.security.cert.PKIXParameters;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import edu.umd.cs.findbugs.annotations.CheckForNull;
|
||||
import edu.umd.cs.findbugs.annotations.Nullable;
|
||||
import jakarta.mail.Message;
|
||||
import jakarta.mail.MessagingException;
|
||||
|
@ -353,85 +351,6 @@ public final class EmailProcessor {
|
|||
return new ResponseGenerator(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an optional property from the message.
|
||||
* <p>
|
||||
* Optional property means: If there is a signed message, try to fetch the property
|
||||
* from there. If the property is not present, fetch it from the unsigned message
|
||||
* instead. If it's also not there, return {@code null}.
|
||||
*
|
||||
* @param getter
|
||||
* The getter method of {@link Message} to be invoked
|
||||
* @param message
|
||||
* The outer (unsigned) {@link Message} that serves as fallback
|
||||
* @param signedMessage
|
||||
* The signed (inner) {@link Message} where the property is looked up first
|
||||
* @param <T>
|
||||
* The expected result type
|
||||
* @return The mail property, or {@code null} if not found
|
||||
*/
|
||||
@CheckForNull
|
||||
private <T> T getOptional(MessageFunction<Message, T> getter, Message message, @Nullable Message signedMessage)
|
||||
throws MessagingException {
|
||||
if (signedMessage != null) {
|
||||
T result = getter.apply(signedMessage);
|
||||
if (result != null) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
return getter.apply(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a mandatory property from the message.
|
||||
* <p>
|
||||
* Mandatory means: If there is a signed message, the property <em>must</em> be
|
||||
* present there. The unsigned message is only queried as fallback if there is no
|
||||
* signed message at all.
|
||||
*
|
||||
* @param getter
|
||||
* The getter method of {@link Message} to be invoked
|
||||
* @param message
|
||||
* The outer (unsigned) {@link Message} that serves as fallback if there is
|
||||
* no signed message.
|
||||
* @param signedMessage
|
||||
* The signed (inner) {@link Message} where the property is expected, or
|
||||
* {@code null} if there is no signed message.
|
||||
* @param header
|
||||
* Name of the expected header
|
||||
* @param <T>
|
||||
* The expected result type
|
||||
* @return The mail property, or {@code null} if not found
|
||||
*/
|
||||
@CheckForNull
|
||||
private <T> T getMandatory(MessageFunction<Message, T> getter, Message message, @Nullable Message signedMessage, String header)
|
||||
throws MessagingException, AcmeInvalidMessageException {
|
||||
if (signedMessage != null) {
|
||||
T value = getter.apply(signedMessage);
|
||||
if (value == null) {
|
||||
throw new AcmeInvalidMessageException("Protected header '" + header + "' expected, but missing.");
|
||||
}
|
||||
return value;
|
||||
}
|
||||
return getter.apply(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if this message is "auto-generated".
|
||||
*
|
||||
* @param autoSubmitted
|
||||
* Auto-Submitted header content
|
||||
* @return {@code true} if the mail was auto-generated.
|
||||
*/
|
||||
private boolean isAutoGenerated(@Nullable String[] autoSubmitted) throws MessagingException {
|
||||
if (autoSubmitted == null || autoSubmitted.length == 0) {
|
||||
return false;
|
||||
}
|
||||
return Arrays.stream(autoSubmitted)
|
||||
.map(String::trim)
|
||||
.anyMatch(h -> h.startsWith("auto-generated"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a challenge has been set. Throws an exception if not.
|
||||
*/
|
||||
|
@ -441,12 +360,6 @@ public final class EmailProcessor {
|
|||
}
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
private interface MessageFunction<M extends Message, R> {
|
||||
@CheckForNull
|
||||
R apply(M message) throws MessagingException;
|
||||
}
|
||||
|
||||
/**
|
||||
* A builder for {@link EmailProcessor}.
|
||||
* <p>
|
||||
|
@ -456,7 +369,7 @@ public final class EmailProcessor {
|
|||
*/
|
||||
public static class Builder {
|
||||
private boolean unsigned = false;
|
||||
private SignedMailBuilder builder = new SignedMailBuilder();
|
||||
private final SignedMailBuilder builder = new SignedMailBuilder();
|
||||
|
||||
private Builder() {
|
||||
// Private constructor
|
||||
|
|
|
@ -93,7 +93,7 @@ public class ResponseGenerator {
|
|||
* Sets a {@link ResponseBodyGenerator} that is used for generating a response body.
|
||||
* <p>
|
||||
* Use this generator to individually style the email body, for example to use a
|
||||
* multipart body. However be aware that the response mail is evaluated by a machine,
|
||||
* multipart body. However, be aware that the response mail is evaluated by a machine,
|
||||
* and usually not read by humans, so the body should be designed as simple as
|
||||
* possible.
|
||||
* <p>
|
||||
|
|
|
@ -30,9 +30,9 @@ import jakarta.mail.internet.AddressException;
|
|||
import jakarta.mail.internet.InternetAddress;
|
||||
import org.assertj.core.api.AutoCloseableSoftAssertions;
|
||||
import org.bouncycastle.asn1.ASN1Encodable;
|
||||
import org.bouncycastle.asn1.ASN1IA5String;
|
||||
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
|
||||
import org.bouncycastle.asn1.DERBitString;
|
||||
import org.bouncycastle.asn1.DERIA5String;
|
||||
import org.bouncycastle.asn1.pkcs.Attribute;
|
||||
import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
|
||||
import org.bouncycastle.asn1.x500.X500Name;
|
||||
|
@ -55,14 +55,12 @@ import org.shredzone.acme4j.util.KeyPairUtils;
|
|||
public class SMIMECSRBuilderTest {
|
||||
|
||||
private static KeyPair testKey;
|
||||
private static KeyPair testEcKey;
|
||||
|
||||
@BeforeAll
|
||||
public static void setup() {
|
||||
Security.addProvider(new BouncyCastleProvider());
|
||||
|
||||
testKey = KeyPairUtils.createKeyPair(512);
|
||||
testEcKey = KeyPairUtils.createECKeyPair("secp256r1");
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -160,21 +158,21 @@ public class SMIMECSRBuilderTest {
|
|||
assertThat(builder.toString()).isEqualTo(",TYPE=SIGNING_AND_ENCRYPTION");
|
||||
|
||||
assertThatExceptionOfType(NullPointerException.class)
|
||||
.isThrownBy(() -> new SMIMECSRBuilder().addValue((String) null, "value"))
|
||||
.as("addValue(String, String)");
|
||||
.as("addValue(String, String)")
|
||||
.isThrownBy(() -> new SMIMECSRBuilder().addValue((String) null, "value"));
|
||||
assertThatExceptionOfType(NullPointerException.class)
|
||||
.isThrownBy(() -> new SMIMECSRBuilder().addValue((ASN1ObjectIdentifier) null, "value"))
|
||||
.as("addValue(ASN1ObjectIdentifier, String)");
|
||||
.as("addValue(ASN1ObjectIdentifier, String)")
|
||||
.isThrownBy(() -> new SMIMECSRBuilder().addValue((ASN1ObjectIdentifier) null, "value"));
|
||||
assertThatExceptionOfType(NullPointerException.class)
|
||||
.isThrownBy(() -> new SMIMECSRBuilder().addValue("C", null))
|
||||
.as("addValue(String, null)");
|
||||
assertThatExceptionOfType(IllegalArgumentException.class)
|
||||
.isThrownBy(() -> new SMIMECSRBuilder().addValue("UNKNOWNATT", "val"))
|
||||
.as("addValue(String, null)")
|
||||
.isThrownBy(() -> new SMIMECSRBuilder().addValue("C", null));
|
||||
assertThatExceptionOfType(IllegalArgumentException.class)
|
||||
.as("addValue(String, null)")
|
||||
.isThrownBy(() -> new SMIMECSRBuilder().addValue("UNKNOWNATT", "val"))
|
||||
.withMessage(invAttNameExMessage);
|
||||
assertThatExceptionOfType(AddressException.class)
|
||||
.isThrownBy(() -> new SMIMECSRBuilder().addValue("CN", "invalid@example..com"))
|
||||
.as("addValue(String, invalid String)");
|
||||
.as("addValue(String, invalid String)")
|
||||
.isThrownBy(() -> new SMIMECSRBuilder().addValue("CN", "invalid@example..com"));
|
||||
|
||||
assertThat(builder.toString()).isEqualTo(",TYPE=SIGNING_AND_ENCRYPTION");
|
||||
|
||||
|
@ -238,7 +236,7 @@ public class SMIMECSRBuilderTest {
|
|||
GeneralNames names = GeneralNames.fromExtensions((Extensions) extensions[0], Extension.subjectAlternativeName);
|
||||
assertThat(names.getNames())
|
||||
.filteredOn(gn -> gn.getTagNo() == GeneralName.rfc822Name)
|
||||
.extracting(gn -> DERIA5String.getInstance(gn.getName()).getString())
|
||||
.extracting(gn -> ASN1IA5String.getInstance(gn.getName()).getString())
|
||||
.containsExactlyInAnyOrder("mail@example.com", "info@example.com",
|
||||
"sales@example.com", "shop@example.com", "support@example.com",
|
||||
"help@example.com");
|
||||
|
@ -307,7 +305,7 @@ public class SMIMECSRBuilderTest {
|
|||
* Make sure an exception is thrown when nothing is set.
|
||||
*/
|
||||
@Test
|
||||
public void testNoEmail() throws IOException {
|
||||
public void testNoEmail() {
|
||||
assertThrows(IllegalStateException.class, () -> {
|
||||
SMIMECSRBuilder builder = new SMIMECSRBuilder();
|
||||
builder.sign(testKey);
|
||||
|
@ -318,7 +316,7 @@ public class SMIMECSRBuilderTest {
|
|||
* Make sure all getters will fail if the CSR is not signed.
|
||||
*/
|
||||
@Test
|
||||
public void testNoSign() throws IOException {
|
||||
public void testNoSign() {
|
||||
SMIMECSRBuilder builder = new SMIMECSRBuilder();
|
||||
|
||||
assertThrows(IllegalStateException.class, builder::getCSR, "getCSR");
|
||||
|
|
Loading…
Reference in New Issue