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