Add support for S/MIME signature validation

pull/134/head
Richard Körber 2022-11-18 15:56:06 +01:00
parent 1ee68e1e09
commit 636ccc37e0
No known key found for this signature in database
GPG Key ID: AAB9FD19C78AA3E0
28 changed files with 1291 additions and 52 deletions

View File

@ -27,7 +27,7 @@ It is an independent open source implementation that is not affiliated with or e
* [jose4j](https://bitbucket.org/b_c/jose4j/wiki/Home)
* [slf4j](http://www.slf4j.org/)
* For `acme4j-utils`: [Bouncy Castle](https://www.bouncycastle.org/)
* For `acme4j-smime`: [Jakarta Mail](https://eclipse-ee4j.github.io/mail/)
* For `acme4j-smime`: [Jakarta Mail](https://eclipse-ee4j.github.io/mail/), [Bouncy Castle](https://www.bouncycastle.org/)
## Usage

1
acme4j-smime/.gitattributes vendored Normal file
View File

@ -0,0 +1 @@
*.eml text eol=crlf

View File

@ -49,6 +49,11 @@
<artifactId>bcpkix-jdk18on</artifactId>
<version>${bouncycastle.version}</version>
</dependency>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcjmail-jdk18on</artifactId>
<version>${bouncycastle.version}</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>

View File

@ -18,13 +18,17 @@ module org.shredzone.acme4j.smime {
requires transitive jakarta.mail;
requires static com.github.spotbugs.annotations;
requires org.bouncycastle.util;
requires org.bouncycastle.mail;
requires org.bouncycastle.pkix;
requires org.bouncycastle.provider;
requires org.slf4j;
exports org.shredzone.acme4j.smime;
exports org.shredzone.acme4j.smime.challenge;
exports org.shredzone.acme4j.smime.csr;
exports org.shredzone.acme4j.smime.email;
exports org.shredzone.acme4j.smime.exception;
provides org.shredzone.acme4j.provider.ChallengeProvider
with org.shredzone.acme4j.smime.challenge.EmailReply00ChallengeProvider;

View File

@ -16,38 +16,56 @@ package org.shredzone.acme4j.smime.email;
import static java.util.Objects.requireNonNull;
import static jakarta.mail.Message.RecipientType.TO;
import java.io.IOException;
import java.net.URL;
import java.security.cert.CertificateParsingException;
import java.security.cert.X509Certificate;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.atomic.AtomicReference;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import edu.umd.cs.findbugs.annotations.CheckForNull;
import edu.umd.cs.findbugs.annotations.Nullable;
import jakarta.mail.Address;
import jakarta.mail.Message;
import jakarta.mail.MessagingException;
import jakarta.mail.Session;
import jakarta.mail.internet.AddressException;
import jakarta.mail.internet.InternetAddress;
import jakarta.mail.internet.MimeMessage;
import jakarta.mail.internet.MimeMultipart;
import org.bouncycastle.cms.CMSException;
import org.bouncycastle.cms.SignerInformation;
import org.bouncycastle.cms.SignerInformationVerifier;
import org.bouncycastle.cms.jcajce.JcaSimpleSignerInfoVerifierBuilder;
import org.bouncycastle.mail.smime.SMIMESigned;
import org.bouncycastle.operator.OperatorCreationException;
import org.shredzone.acme4j.Identifier;
import org.shredzone.acme4j.Login;
import org.shredzone.acme4j.exception.AcmeProtocolException;
import org.shredzone.acme4j.smime.challenge.EmailReply00Challenge;
import org.shredzone.acme4j.smime.exception.AcmeInvalidMessageException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* A processor for incoming "Challenge" emails.
* <p>
* Note that according to RFC-8823, the incoming mail must be DKIM or S/MIME signed, and
* the signature must be validated. This is <em>not</em> done by this processor, because
* it is usually checked by the inbound MTA.
*
* @see <a href="https://datatracker.ietf.org/doc/html/rfc8823">RFC 8823</a>
* @since 2.12
*/
public final class EmailProcessor {
private static final Logger LOG = LoggerFactory.getLogger(EmailProcessor.class);
private static final Pattern SUBJECT_PATTERN = Pattern.compile("ACME:\\s+([0-9A-Za-z_\\s-]+=?)\\s*");
private static final int RFC822NAME = 1;
private final String token1;
private final Optional<String> messageId;
@ -56,42 +74,186 @@ public final class EmailProcessor {
private final Collection<InternetAddress> replyTo;
private final AtomicReference<EmailReply00Challenge> challengeRef = new AtomicReference<>();
/**
* Processes the given e-mail message.
* <p>
* Note that according to RFC-8823, the challenge message must be signed using either
* DKIM or S/MIME. This method does not do any DKIM or S/MIME validation, and assumes
* that this has already been done by the inbound MTA.
*
* @param message
* E-mail that was received from the CA. The inbound MTA has already taken
* care of DKIM and/or S/MIME validation.
* @return EmailProcessor for this e-mail
* @throws AcmeInvalidMessageException
* if a validation failed, and the message <em>must</em> be rejected.
* @since 2.15
*/
public static EmailProcessor plainMessage(Message message)
throws AcmeInvalidMessageException {
return new EmailProcessor(message, null, false, null);
}
/**
* Performs an S/MIME validation and processes the given e-mail message.
* <p>
* The owner of the given certificate must be the sender of that email.
*
* @param message
* E-mail that was received from the CA.
* @param mailSession
* A {@link Session} that can be used for processing inner e-mails.
* @param signCert
* The signing certificate of the sender.
* @param strict
* If {@code true}, the S/MIME protected headers "From", "To", and "Subject"
* <em>must</em> match the headers of the received message. If {@code false},
* only the S/MIME protected headers are used, and the headers of the received
* message are ignored.
* @return EmailProcessor for this e-mail
* @throws AcmeInvalidMessageException
* if a validation failed, and the message <em>must</em> be rejected.
* @since 2.15
*/
public static EmailProcessor smimeMessage(Message message, Session mailSession,
X509Certificate signCert, boolean strict)
throws AcmeInvalidMessageException {
try {
if (!(message instanceof MimeMessage)) {
throw new AcmeInvalidMessageException("Not a S/MIME message");
}
MimeMessage mimeMessage = (MimeMessage) message;
if (!(mimeMessage.getContent() instanceof MimeMultipart)) {
throw new AcmeProtocolException("S/MIME signed email must contain MimeMultipart");
}
MimeMultipart mp = (MimeMultipart) message.getContent();
SMIMESigned signed = new SMIMESigned(mp);
SignerInformationVerifier verifier = new JcaSimpleSignerInfoVerifierBuilder().build(signCert);
boolean hasMatch = false;
for (SignerInformation signer : signed.getSignerInfos().getSigners()) {
hasMatch |= signer.verify(verifier);
}
if (!hasMatch) {
throw new AcmeInvalidMessageException("The S/MIME signature is invalid");
}
MimeMessage content = signed.getContentAsMimeMessage(mailSession);
if (!content.getContentType().equalsIgnoreCase("message/rfc822; forwarded=no")) {
throw new AcmeInvalidMessageException("Message does not contain protected headers");
}
MimeMessage body = new MimeMessage(mailSession, content.getInputStream());
List<Address> validFromAddresses = Optional.ofNullable(signCert.getSubjectAlternativeNames())
.orElseGet(Collections::emptyList)
.stream()
.filter(l -> ((Number) l.get(0)).intValue() == RFC822NAME)
.map(l -> l.get(1).toString())
.map(l -> {
try {
return new InternetAddress(l);
} catch (AddressException ex) {
// Ignore invalid email addresses
LOG.debug("Certificate contains invalid e-mail address {}", l, ex);
return null;
}
})
.filter(Objects::nonNull)
.collect(Collectors.toList());
if (validFromAddresses.isEmpty()) {
throw new AcmeInvalidMessageException("Signing certificate does not provide a rfc822Name subjectAltName");
}
return new EmailProcessor(message, body, strict, validFromAddresses);
} catch (IOException | MessagingException | CMSException | OperatorCreationException |
CertificateParsingException ex) {
throw new AcmeInvalidMessageException("Invalid S/MIME mail", ex);
}
}
/**
* Creates a new {@link EmailProcessor} for the incoming "Challenge" message.
* <p>
* The incoming message is validated against the requirements of RFC-8823. An {@link
* AcmeProtocolException} is thrown if the validation fails. DKIM or S/MIME signature
* is <em>not</em> checked by the processor, and must be checked elsewhere (usually by
* the inbound MTA).
* The incoming message is validated against the requirements of RFC-8823.
*
* @param message
* "Challenge" message as it was sent by the CA.
* @throws AcmeProtocolException
* if the incoming message is not a valid "challenge" message according to
* RFC-8823.
* @param signedMessage
* The signed part of the challenge message if present, or {@code null}. The
* signature is assumed to be valid, and must be validated in a previous
* step.
* @param strict
* If {@code true}, the S/MIME protected headers "From", "To", and "Subject"
* <em>must</em> match the headers of the received message. If {@code false},
* only the S/MIME protected headers are used, and the headers of the received
* message are ignored.
* @param validFromAddresses
* A {@link List} of {@link Address} that were found in the certificate's
* rfc822Name subjectAltName extension. The mail's From address <em>must</em>
* be found in this list, otherwise the signed message will be rejected.
* {@code null} to disable this validation step.
* @throws AcmeInvalidMessageException
* if a validation failed, and the message <em>must</em> be rejected.
*/
public EmailProcessor(Message message) {
private EmailProcessor(Message message, @Nullable MimeMessage signedMessage,
boolean strict, @Nullable List<Address> validFromAddresses)
throws AcmeInvalidMessageException {
requireNonNull(message, "message");
// Validate challenge and extract token 1
try {
if (!isAutoGenerated(message)) {
throw new AcmeProtocolException("Message is not auto-generated");
if (!isAutoGenerated(getOptional(m -> m.getHeader("Auto-Submitted"), message, signedMessage))) {
throw new AcmeInvalidMessageException("Message is not auto-generated");
}
Address[] from = message.getFrom();
Address[] from = getMandatory(Message::getFrom, message, signedMessage, "From");
if (from == null) {
throw new AcmeInvalidMessageException("Message has no 'From' header");
}
if (from.length != 1) {
throw new AcmeProtocolException("Message must have exactly one sender, but has " + from.length);
throw new AcmeInvalidMessageException("Message must have exactly one sender, but has " + from.length);
}
if (validFromAddresses != null && !validFromAddresses.contains(from[0])) {
throw new AcmeInvalidMessageException("Sender '" + from[0] + "' was not found in signing certificate");
}
if (strict && signedMessage != null) {
Address[] outerFrom = message.getFrom();
if ((outerFrom.length > 1) || (outerFrom.length == 1 && outerFrom[0] != null
&& !outerFrom[0].equals(from[0]))) {
throw new AcmeInvalidMessageException("Protected 'From' header does not match envelope header");
}
}
sender = new InternetAddress(from[0].toString());
Address[] to = message.getRecipients(TO);
Address[] to = getMandatory(m -> m.getRecipients(TO), message, signedMessage, "To");
if (to == null) {
throw new AcmeInvalidMessageException("Message has no 'To' header");
}
if (to.length != 1) {
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]))) {
throw new AcmeInvalidMessageException("Protected 'To' header does not match envelope header");
}
}
recipient = new InternetAddress(to[0].toString());
String subject = message.getSubject();
String subject = getMandatory(Message::getSubject, message, signedMessage, "Subject");
if (subject == null) {
throw new AcmeInvalidMessageException("Message has no 'Subject' header");
}
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);
if (!m.matches()) {
throw new AcmeProtocolException("Invalid subject: " + subject);
@ -99,7 +261,7 @@ public final class EmailProcessor {
// white spaces within the token part must be ignored
this.token1 = m.group(1).replaceAll("\\s+", "");
Address[] rto = message.getReplyTo();
Address[] rto = getOptional(Message::getReplyTo, message, signedMessage);
if (rto != null) {
replyTo = Collections.unmodifiableList(Arrays.stream(rto)
.filter(InternetAddress.class::isInstance)
@ -109,7 +271,7 @@ public final class EmailProcessor {
replyTo = Collections.emptyList();
}
String[] mid = message.getHeader("Message-ID");
String[] mid = getOptional(n -> n.getHeader("Message-ID"), message, signedMessage);
if (mid != null && mid.length > 0) {
messageId = Optional.of(mid[0]);
} else {
@ -297,16 +459,78 @@ 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 message
* Message to check.
* @param autoSubmitted
* Auto-Submitted header content
* @return {@code true} if the mail was auto-generated.
*/
private boolean isAutoGenerated(Message message) throws MessagingException {
String[] autoSubmitted = message.getHeader("Auto-Submitted");
if (autoSubmitted == null) {
private boolean isAutoGenerated(@Nullable String[] autoSubmitted) throws MessagingException {
if (autoSubmitted == null || autoSubmitted.length == 0) {
return false;
}
return Arrays.stream(autoSubmitted)
@ -323,4 +547,10 @@ public final class EmailProcessor {
}
}
@FunctionalInterface
private interface MessageFunction<M extends Message, R> {
@CheckForNull
R apply(M message) throws MessagingException;
}
}

View File

@ -28,9 +28,8 @@ import jakarta.mail.internet.MimeMessage;
/**
* A helper for creating an email response to the "challenge" email.
* <p>
* According to RFC-8823, the response email must have a DKIM or S/MIME signature. This is
* <em>not</em> done by the response generator, because it is usually performed by the
* outbound MTA.
* According to RFC-8823, the response email <em>must</em> be DKIM signed. This is
* <em>not</em> done by the response generator, but must be done by the outbound MTA.
*
* @see <a href="https://datatracker.ietf.org/doc/html/rfc8823">RFC 8823</a>
* @since 2.12

View File

@ -0,0 +1,57 @@
/*
* acme4j - Java ACME client
*
* Copyright (C) 2022 Richard "Shred" Körber
* http://acme4j.shredzone.org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*/
package org.shredzone.acme4j.smime.exception;
import org.shredzone.acme4j.exception.AcmeException;
/**
* This exception is thrown when the challenge message is invalid.
* <p>
* If this exception is thrown, the challenge message does not match the actual challenge,
* and <em>must</em> be rejected.
* <p>
* Reasons may be:
* <ul>
* <li>Unexpected sender address</li>
* <li>Bad S/MIME signature</li>
* </ul>
*
* @since 2.15
*/
public class AcmeInvalidMessageException extends AcmeException {
private static final long serialVersionUID = 5607857024718309330L;
/**
* Creates a new {@link AcmeInvalidMessageException}.
*
* @param msg
* Reason of the exception
*/
public AcmeInvalidMessageException(String msg) {
super(msg);
}
/**
* Creates a new {@link AcmeInvalidMessageException}.
*
* @param msg
* Reason of the exception
* @param cause
* Cause
*/
public AcmeInvalidMessageException(String msg, Throwable cause) {
super(msg, cause);
}
}

View File

@ -0,0 +1,23 @@
/*
* acme4j - Java ACME client
*
* Copyright (C) 2022 Richard "Shred" Körber
* http://acme4j.shredzone.org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*/
@ReturnValuesAreNonnullByDefault
@DefaultAnnotationForParameters(NonNull.class)
@DefaultAnnotationForFields(NonNull.class)
package org.shredzone.acme4j.smime.exception;
import edu.umd.cs.findbugs.annotations.DefaultAnnotationForFields;
import edu.umd.cs.findbugs.annotations.DefaultAnnotationForParameters;
import edu.umd.cs.findbugs.annotations.NonNull;
import edu.umd.cs.findbugs.annotations.ReturnValuesAreNonnullByDefault;

View File

@ -23,6 +23,9 @@ import java.io.Reader;
import java.io.UncheckedIOException;
import java.nio.charset.StandardCharsets;
import java.security.KeyPair;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.Properties;
import jakarta.mail.Message;
@ -125,4 +128,20 @@ public abstract class SMIMETests {
}
}
/**
* Reads a certificate from the given resource.
*
* @param name
* Resource name of the certificate
* @return X509Certificate that was read
*/
protected X509Certificate readCertificate(String name) throws IOException {
try (InputStream in = SMIMETests.class.getResourceAsStream("/" + name + ".pem")) {
CertificateFactory cf = CertificateFactory.getInstance("X.509");
return (X509Certificate) cf.generateCertificate(in);
} catch (CertificateException ex) {
throw new IOException(ex);
}
}
}

View File

@ -14,21 +14,27 @@
package org.shredzone.acme4j.smime.email;
import static jakarta.mail.Message.RecipientType.TO;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.*;
import static org.junit.jupiter.api.Assertions.assertThrows;
import java.io.IOException;
import java.security.Security;
import java.security.cert.X509Certificate;
import java.util.Optional;
import jakarta.mail.Message;
import jakarta.mail.MessagingException;
import jakarta.mail.internet.InternetAddress;
import jakarta.mail.internet.MimeMessage;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.shredzone.acme4j.Identifier;
import org.shredzone.acme4j.exception.AcmeProtocolException;
import org.shredzone.acme4j.smime.EmailIdentifier;
import org.shredzone.acme4j.smime.SMIMETests;
import org.shredzone.acme4j.smime.challenge.EmailReply00Challenge;
import org.shredzone.acme4j.smime.exception.AcmeInvalidMessageException;
/**
* Unit tests for {@link EmailProcessor} and {@link ResponseGenerator}.
@ -40,9 +46,14 @@ public class EmailProcessorTest extends SMIMETests {
private final InternetAddress expectedReplyTo = email("acme-validator@example.org");
private final Message message = mockMessage("challenge");
@BeforeAll
public static void setup() {
Security.addProvider(new BouncyCastleProvider());
}
@Test
public void testEmailParser() throws MessagingException {
EmailProcessor processor = new EmailProcessor(message);
public void testEmailParser() throws AcmeInvalidMessageException {
EmailProcessor processor = EmailProcessor.plainMessage(message);
processor.expectedFrom(expectedFrom);
processor.expectedTo(expectedTo);
processor.expectedIdentifier(EmailIdentifier.email(expectedTo));
@ -55,10 +66,93 @@ public class EmailProcessorTest extends SMIMETests {
assertThat(processor.getReplyTo()).contains(email("acme-validator@example.org"));
}
@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);
}
@Test
public void testInvalidSignature() {
assertThatExceptionOfType(AcmeInvalidMessageException.class)
.isThrownBy(() -> {
MimeMessage message = (MimeMessage) mockMessage("invalid-signed-mail");
X509Certificate certificate = readCertificate("valid-signer");
EmailProcessor processor = EmailProcessor.smimeMessage(message, mailSession, certificate, true);
})
.withMessage("The S/MIME signature is invalid");
}
@Test
public void testValidSignatureButNoSAN() {
assertThatExceptionOfType(AcmeInvalidMessageException.class)
.isThrownBy(() -> {
MimeMessage message = (MimeMessage) mockMessage("invalid-nosan");
X509Certificate certificate = readCertificate("valid-signer-nosan");
EmailProcessor processor = EmailProcessor.smimeMessage(message, mailSession, certificate, true);
})
.withMessage("Signing certificate does not provide a rfc822Name subjectAltName");
}
@Test
public void testSANDoesNotMatchFrom() {
assertThatExceptionOfType(AcmeInvalidMessageException.class)
.isThrownBy(() -> {
MimeMessage message = (MimeMessage) mockMessage("invalid-cert-mismatch");
X509Certificate certificate = readCertificate("valid-signer");
EmailProcessor processor = EmailProcessor.smimeMessage(message, mailSession, certificate, true);
})
.withMessage("Sender 'different-ca@example.com' was not found in signing certificate");
}
@Test
public void testInvalidProtectedFromHeader() {
assertThatExceptionOfType(AcmeInvalidMessageException.class)
.isThrownBy(() -> {
MimeMessage message = (MimeMessage) mockMessage("invalid-protected-mail-from");
X509Certificate certificate = readCertificate("valid-signer");
EmailProcessor processor = EmailProcessor.smimeMessage(message, mailSession, certificate, true);
})
.withMessage("Protected 'From' header does not match envelope header");
}
@Test
public void testInvalidProtectedToHeader() {
assertThatExceptionOfType(AcmeInvalidMessageException.class)
.isThrownBy(() -> {
MimeMessage message = (MimeMessage) mockMessage("invalid-protected-mail-to");
X509Certificate certificate = readCertificate("valid-signer");
EmailProcessor processor = EmailProcessor.smimeMessage(message, mailSession, certificate, true);
})
.withMessage("Protected 'To' header does not match envelope header");
}
@Test
public void testInvalidProtectedSubjectHeader() {
assertThatExceptionOfType(AcmeInvalidMessageException.class)
.isThrownBy(() -> {
MimeMessage message = (MimeMessage) mockMessage("invalid-protected-mail-subject");
X509Certificate certificate = readCertificate("valid-signer");
EmailProcessor processor = EmailProcessor.smimeMessage(message, mailSession, certificate, true);
})
.withMessage("Protected 'Subject' header does not match envelope header");
}
@Test
public void testNonStrictInvalidProtectedSubjectHeader() {
assertThatNoException()
.isThrownBy(() -> {
MimeMessage message = (MimeMessage) mockMessage("invalid-protected-mail-subject");
X509Certificate certificate = readCertificate("valid-signer");
EmailProcessor processor = EmailProcessor.smimeMessage(message, mailSession, certificate, false);
});
}
@Test
public void textExpectedFromFails() {
assertThrows(AcmeProtocolException.class, () -> {
EmailProcessor processor = new EmailProcessor(message);
EmailProcessor processor = EmailProcessor.plainMessage(message);
processor.expectedFrom(expectedTo);
});
}
@ -66,7 +160,7 @@ public class EmailProcessorTest extends SMIMETests {
@Test
public void textExpectedToFails() {
assertThrows(AcmeProtocolException.class, () -> {
EmailProcessor processor = new EmailProcessor(message);
EmailProcessor processor = EmailProcessor.plainMessage(message);
processor.expectedTo(expectedFrom);
});
}
@ -74,7 +168,7 @@ public class EmailProcessorTest extends SMIMETests {
@Test
public void textExpectedIdentifierFails1() {
assertThrows(AcmeProtocolException.class, () -> {
EmailProcessor processor = new EmailProcessor(message);
EmailProcessor processor = EmailProcessor.plainMessage(message);
processor.expectedIdentifier(EmailIdentifier.email(expectedFrom));
});
}
@ -82,7 +176,7 @@ public class EmailProcessorTest extends SMIMETests {
@Test
public void textExpectedIdentifierFails2() {
assertThrows(AcmeProtocolException.class, () -> {
EmailProcessor processor = new EmailProcessor(message);
EmailProcessor processor = EmailProcessor.plainMessage(message);
processor.expectedIdentifier(Identifier.ip("192.168.0.1"));
});
}
@ -90,7 +184,7 @@ public class EmailProcessorTest extends SMIMETests {
@Test
public void textNoChallengeFails1() {
assertThrows(IllegalStateException.class, () -> {
EmailProcessor processor = new EmailProcessor(message);
EmailProcessor processor = EmailProcessor.plainMessage(message);
processor.getToken();
});
}
@ -98,7 +192,7 @@ public class EmailProcessorTest extends SMIMETests {
@Test
public void textNoChallengeFails2() {
assertThrows(IllegalStateException.class, () -> {
EmailProcessor processor = new EmailProcessor(message);
EmailProcessor processor = EmailProcessor.plainMessage(message);
processor.getAuthorization();
});
}
@ -106,16 +200,16 @@ public class EmailProcessorTest extends SMIMETests {
@Test
public void textNoChallengeFails3() {
assertThrows(IllegalStateException.class, () -> {
EmailProcessor processor = new EmailProcessor(message);
EmailProcessor processor = EmailProcessor.plainMessage(message);
processor.respond();
});
}
@Test
public void testChallenge() {
public void testChallenge() throws AcmeInvalidMessageException {
EmailReply00Challenge challenge = mockChallenge("emailReplyChallenge");
EmailProcessor processor = new EmailProcessor(message);
EmailProcessor processor = EmailProcessor.plainMessage(message);
processor.withChallenge(challenge);
assertThat(processor.getToken()).isEqualTo(TOKEN);
assertThat(processor.getAuthorization()).isEqualTo(KEY_AUTHORIZATION);
@ -126,16 +220,16 @@ public class EmailProcessorTest extends SMIMETests {
public void testChallengeMismatch() {
assertThrows(AcmeProtocolException.class, () -> {
EmailReply00Challenge challenge = mockChallenge("emailReplyChallengeMismatch");
EmailProcessor processor = new EmailProcessor(message);
EmailProcessor processor = EmailProcessor.plainMessage(message);
processor.withChallenge(challenge);
});
}
@Test
public void testResponse() throws IOException, MessagingException {
public void testResponse() throws IOException, MessagingException, AcmeInvalidMessageException {
EmailReply00Challenge challenge = mockChallenge("emailReplyChallenge");
Message response = new EmailProcessor(message)
Message response = EmailProcessor.plainMessage(message)
.withChallenge(challenge)
.respond()
.generateResponse(mailSession);
@ -144,10 +238,10 @@ public class EmailProcessorTest extends SMIMETests {
}
@Test
public void testResponseWithHeaderFooter() throws IOException, MessagingException {
public void testResponseWithHeaderFooter() throws IOException, MessagingException, AcmeInvalidMessageException {
EmailReply00Challenge challenge = mockChallenge("emailReplyChallenge");
Message response = new EmailProcessor(message)
Message response = EmailProcessor.plainMessage(message)
.withChallenge(challenge)
.respond()
.withHeader("This is an introduction.")
@ -161,10 +255,10 @@ public class EmailProcessorTest extends SMIMETests {
}
@Test
public void testResponseWithCallback() throws IOException, MessagingException {
public void testResponseWithCallback() throws IOException, MessagingException, AcmeInvalidMessageException {
EmailReply00Challenge challenge = mockChallenge("emailReplyChallenge");
Message response = new EmailProcessor(message)
Message response = EmailProcessor.plainMessage(message)
.withChallenge(challenge)
.respond()
.withGenerator((msg, body) -> msg.setContent("Head\r\n" + body + "Foot", "text/plain"))

View File

@ -0,0 +1,67 @@
From: different-ca@example.org
To: recipient@example.org
Subject: ACME: LgYemJLy3F1LDkiJrdIGbEzyFJyOyf6vBdyZ1TG3sME=
Auto-Submitted: auto-generated; type=acme
Message-ID: <A2299BB.FF7788@example.org>
MIME-Version: 1.0
Content-Type: multipart/signed; protocol="application/x-pkcs7-signature"; micalg="sha1"; boundary="----6B6A5C5DBC60D7D16B6C08BF092D4185"
This is an S/MIME signed message
------6B6A5C5DBC60D7D16B6C08BF092D4185
Content-Type: message/RFC822; forwarded=no
From: different-ca@example.com
To: recipient@example.org
Subject: ACME: LgYemJLy3F1LDkiJrdIGbEzyFJyOyf6vBdyZ1TG3sME=
Message-ID: <A2299BB.FF7788@example.org>
MIME-Version: 1.0
Content-Type: text/plain; charset=utf-8
This is an automatically generated ACME challenge.
------6B6A5C5DBC60D7D16B6C08BF092D4185
Content-Type: application/x-pkcs7-signature; name="smime.p7s"
Content-Transfer-Encoding: base64
Content-Disposition: attachment; filename="smime.p7s"
MIIGzQYJKoZIhvcNAQcCoIIGvjCCBroCAQExCzAJBgUrDgMCGgUAMAsGCSqGSIb3
DQEHAaCCA/4wggP6MIIC4qADAgECAhQoC/xUcLhcK13sGSiYxuUPf758tDANBgkq
hkiG9w0BAQsFADB8MQswCQYDVQQGEwJYWDESMBAGA1UEBwwJQWNtZSBDaXR5MR4w
HAYDVQQKDBVBY21lIENlcnRpZmljYXRlcyBMdGQxFDASBgNVBAMMC2V4YW1wbGUu
Y29tMSMwIQYJKoZIhvcNAQkBFhR2YWxpZC1jYUBleGFtcGxlLmNvbTAeFw0yMjEx
MDQxMjU1MzlaFw0zMjExMDExMjU1MzlaMHwxCzAJBgNVBAYTAlhYMRIwEAYDVQQH
DAlBY21lIENpdHkxHjAcBgNVBAoMFUFjbWUgQ2VydGlmaWNhdGVzIEx0ZDEUMBIG
A1UEAwwLZXhhbXBsZS5jb20xIzAhBgkqhkiG9w0BCQEWFHZhbGlkLWNhQGV4YW1w
bGUuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAzTyW1VRm+mQH
YU5hZWgzgrPRkRzMLlLoCamlRs5DjGf3zIpo9a/17m60YfIXBJruImUBXxa5lp0Z
qaayty+nZOpS0wBZSqTLuslZ0WuyyyW2DEBNO7jLW58cMn8MfAwMYjDcxtubNb7M
AG9iZRj6wn6tKCsXtYUgAIpNhyPPtDuEZ5df1ecOnvlW2vO+MwytM8DLLtwolET/
tPzOXPHDiyKjij02jyJ1DlZxptKudiKaBeY1WY/W/PpS6fGskTJQc/bZPgE3OP/9
Y9Y1uzZTulkO3R6MlLNdLam32/ehpJvSWyfSbToyC2ejvaXoRChPFcAmTpqA0HPg
h8sudiS14QIDAQABo3QwcjAdBgNVHQ4EFgQUR0gRYoOCPJYqFiPGwB141VTCXuIw
HwYDVR0jBBgwFoAUR0gRYoOCPJYqFiPGwB141VTCXuIwDwYDVR0TAQH/BAUwAwEB
/zAfBgNVHREEGDAWgRR2YWxpZC1jYUBleGFtcGxlLmNvbTANBgkqhkiG9w0BAQsF
AAOCAQEAJDQsNTWxVLNHBrVl6p6IetzeBt64GoWp6OP5C8/HY3dlznfbrn6ZvWBr
Vsnc7VZ49r8r/QvRKRrljkIQuNwaW/LmRxJ1AVGGiorspmrdz0Lf2WOXnLH/+4lR
q5dar5YmGqi9Mo2j5ALg/2MiO1PonuKs1eX9tLZCCeanXFexU0qaeFZelunJ6UUm
BXpaGO1QfECcNnvarudosbe1ve6YGABn8MpAY+8zdYnYHPB8Pojhzvk7/8PMHoPu
njDb3lZT+b8BDmNz+GISCUSHYkdK2rWh+8wqD3T5rOtzTqAPuSNeDNocUF6wzxem
HWqvP7yM3VoXYvn7FA4NCa9mE3k8MzGCApcwggKTAgEBMIGUMHwxCzAJBgNVBAYT
AlhYMRIwEAYDVQQHDAlBY21lIENpdHkxHjAcBgNVBAoMFUFjbWUgQ2VydGlmaWNh
dGVzIEx0ZDEUMBIGA1UEAwwLZXhhbXBsZS5jb20xIzAhBgkqhkiG9w0BCQEWFHZh
bGlkLWNhQGV4YW1wbGUuY29tAhQoC/xUcLhcK13sGSiYxuUPf758tDAJBgUrDgMC
GgUAoIHYMBgGCSqGSIb3DQEJAzELBgkqhkiG9w0BBwEwHAYJKoZIhvcNAQkFMQ8X
DTIyMTEwNDEzMzE1NVowIwYJKoZIhvcNAQkEMRYEFNE5fZJFFgePdLTkBjs3GMcD
M5UyMHkGCSqGSIb3DQEJDzFsMGowCwYJYIZIAWUDBAEqMAsGCWCGSAFlAwQBFjAL
BglghkgBZQMEAQIwCgYIKoZIhvcNAwcwDgYIKoZIhvcNAwICAgCAMA0GCCqGSIb3
DQMCAgFAMAcGBSsOAwIHMA0GCCqGSIb3DQMCAgEoMA0GCSqGSIb3DQEBAQUABIIB
AEN+8Cgvfw/72HPSSgJROYqScFrYTCRF7HTAk22zJCVzuh21rbEiPGLJ9Sy4ak/i
BXvXtkX8YJBGuVYrx6QxoF8vmcwlIPVw9Qoc2FevyfRQD19hP7rd7miZ8LWu0B8v
d54mr7aD5zQADLvQGlxjKvCSM3F8HF1KQrRZrfJbEL9NRrgYD7c8ZEAvisaoEfPO
vRjsj9IzKg/RWhOAmh5n591ZNKVb8k0G+5lyCSuP8m/9k0sE705sVrq4sbgIgtFB
HNVLwYvxb88F//rFosFW/njsnlgFx5hjpLbwKu6Du2Sd7L1oye/xUGlKDY8yuy2m
pwKapV2IpD8TjsL7NZ7rJ5U=
------6B6A5C5DBC60D7D16B6C08BF092D4185--

View File

@ -0,0 +1,66 @@
From: valid-ca@example.com
To: recipient@example.org
Subject: ACME: LgYemJLy3F1LDkiJrdIGbEzyFJyOyf6vBdyZ1TG3sME=
Auto-Submitted: auto-generated; type=acme
Message-ID: <A2299BB.FF7788@example.org>
MIME-Version: 1.0
Content-Type: multipart/signed; protocol="application/x-pkcs7-signature"; micalg="sha1"; boundary="----47B1F5074B8F7A13042C44F61463F58F"
This is an S/MIME signed message
------47B1F5074B8F7A13042C44F61463F58F
Content-Type: message/RFC822; forwarded=no
From: valid-ca@example.com
To: recipient@example.org
Subject: ACME: LgYemJLy3F1LDkiJrdIGbEzyFJyOyf6vBdyZ1TG3sME=
Message-ID: <A2299BB.FF7788@example.org>
MIME-Version: 1.0
Content-Type: text/plain; charset=utf-8
This is an automatically generated ACME challenge.
------47B1F5074B8F7A13042C44F61463F58F
Content-Type: application/x-pkcs7-signature; name="smime.p7s"
Content-Transfer-Encoding: base64
Content-Disposition: attachment; filename="smime.p7s"
MIIGrAYJKoZIhvcNAQcCoIIGnTCCBpkCAQExCzAJBgUrDgMCGgUAMAsGCSqGSIb3
DQEHAaCCA90wggPZMIICwaADAgECAhRPShCzW2lh0D89RfDJ5mos/gMAJjANBgkq
hkiG9w0BAQsFADB8MQswCQYDVQQGEwJYWDESMBAGA1UEBwwJQWNtZSBDaXR5MR4w
HAYDVQQKDBVBY21lIENlcnRpZmljYXRlcyBMdGQxFDASBgNVBAMMC2V4YW1wbGUu
Y29tMSMwIQYJKoZIhvcNAQkBFhR2YWxpZC1jYUBleGFtcGxlLmNvbTAeFw0yMjEx
MDQxMjU1MzlaFw0zMjExMDExMjU1MzlaMHwxCzAJBgNVBAYTAlhYMRIwEAYDVQQH
DAlBY21lIENpdHkxHjAcBgNVBAoMFUFjbWUgQ2VydGlmaWNhdGVzIEx0ZDEUMBIG
A1UEAwwLZXhhbXBsZS5jb20xIzAhBgkqhkiG9w0BCQEWFHZhbGlkLWNhQGV4YW1w
bGUuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2BDJcOmFx6Tu
W58rpO4FXbvF7ZoSpIOi4YWfSmD7LSHUq+xGx/vodijFT1Y6a74o4d+XeYTH/X4j
FO9QPdzZDuG8t3ziFACctptaFywUjgYYJQGyPmfg3hn1Cz72q4tAqegOEwL78NA2
YcEd6sx0udAF80C1QHi/kKBMDgj9AOyNyIZ/rBN8CZSkfkpPYWI99Fl/DOuYnr7k
MN1TUWS1906mPqBslh1YVyp6fdGaL6DdlIY+ZE5c9BhST/t+7eLq7fnB5KB+tDvH
D1qnL858K+5Hjfc9MUYTyffDiJaG9zHkEKi3zd0EGcaf1r+lskRqEIOqROjLSDim
T5Q4CuHCYQIDAQABo1MwUTAdBgNVHQ4EFgQURoDGdRoZP6EOfLXTlNxWPDTRmp8w
HwYDVR0jBBgwFoAURoDGdRoZP6EOfLXTlNxWPDTRmp8wDwYDVR0TAQH/BAUwAwEB
/zANBgkqhkiG9w0BAQsFAAOCAQEAvVI9Yj8lL0cvdNY3RD/GQ/tQCGBAzoFTODJU
wn4zE/LfiXfu8SxJhzcpjzKc2j+mxuwGh0OqraIO2FkpO23+X1gCdCt+ClE/6nMs
8UMo4H1wMYYGhjkoLvsH9Ne5N+91PvLQG97LoLsoy+Y95ws23WyqUJ2g7A7Isk3v
7MJZVH2d93hjbtWQ6+3/PP5zJwubEwiDAYvycODfvAig9+0QBIy+uE7XxnEhKxHJ
pvN3p8NmLya7XH3v92N7M6CioyBqw8HL7I5lt5HBqa/U9USVMMmi9v+tFLZYyd7r
7acw6hB7MDcLmtEu08Cgo89K23oTm1JBJZrjZUFbYcYP+fiuizGCApcwggKTAgEB
MIGUMHwxCzAJBgNVBAYTAlhYMRIwEAYDVQQHDAlBY21lIENpdHkxHjAcBgNVBAoM
FUFjbWUgQ2VydGlmaWNhdGVzIEx0ZDEUMBIGA1UEAwwLZXhhbXBsZS5jb20xIzAh
BgkqhkiG9w0BCQEWFHZhbGlkLWNhQGV4YW1wbGUuY29tAhRPShCzW2lh0D89RfDJ
5mos/gMAJjAJBgUrDgMCGgUAoIHYMBgGCSqGSIb3DQEJAzELBgkqhkiG9w0BBwEw
HAYJKoZIhvcNAQkFMQ8XDTIyMTEwNDEzMzE1NVowIwYJKoZIhvcNAQkEMRYEFK+s
B97I9ZGpDGUMWLsE3dCU/pbtMHkGCSqGSIb3DQEJDzFsMGowCwYJYIZIAWUDBAEq
MAsGCWCGSAFlAwQBFjALBglghkgBZQMEAQIwCgYIKoZIhvcNAwcwDgYIKoZIhvcN
AwICAgCAMA0GCCqGSIb3DQMCAgFAMAcGBSsOAwIHMA0GCCqGSIb3DQMCAgEoMA0G
CSqGSIb3DQEBAQUABIIBAEvS2tccMXu/OG8fzgOWsf5MzjBdDD/6doTLuIJS7Ktv
1alpT8ZqSBn4jOgrlM7efTFK8y1vlOdoFZIsfIe+92lclvgHc/2Dw4XB5SswZ59y
fZH+AVtVpzi5oFYiunhBn19vRP9Bri4ma8gCRe7pUwN15Gap8gl3+UQtUY17wcME
H7ALcuG0ETPTxz9p2ueN6FmrthmrDSaZVqW5nyTizgr0zSxicEcwfFz9JZGZFKyp
lPSVrgCwZ1/yaWXlnBXPTdO/DmAvNUAjUk0HZFu+mnelzPPs3c5s4LY3pBNjDPE2
i2hxgRjca2QMsveYdwZn8I/m1P7yatJ3EozHpO3T4Gc=
------47B1F5074B8F7A13042C44F61463F58F--

View File

@ -0,0 +1,67 @@
From: tampered-ca@example.org
To: recipient@example.org
Subject: ACME: LgYemJLy3F1LDkiJrdIGbEzyFJyOyf6vBdyZ1TG3sME=
Auto-Submitted: auto-generated; type=acme
Message-ID: <A2299BB.FF7788@example.org>
MIME-Version: 1.0
Content-Type: multipart/signed; protocol="application/x-pkcs7-signature"; micalg="sha1"; boundary="----1FD9CF28CC0AD72EF1FF6D0511838F0E"
This is an S/MIME signed message
------1FD9CF28CC0AD72EF1FF6D0511838F0E
Content-Type: message/RFC822; forwarded=no
From: valid-ca@example.com
To: recipient@example.org
Subject: ACME: LgYemJLy3F1LDkiJrdIGbEzyFJyOyf6vBdyZ1TG3sME=
Message-ID: <A2299BB.FF7788@example.org>
MIME-Version: 1.0
Content-Type: text/plain; charset=utf-8
This is an automatically generated ACME challenge.
------1FD9CF28CC0AD72EF1FF6D0511838F0E
Content-Type: application/x-pkcs7-signature; name="smime.p7s"
Content-Transfer-Encoding: base64
Content-Disposition: attachment; filename="smime.p7s"
MIIGzQYJKoZIhvcNAQcCoIIGvjCCBroCAQExCzAJBgUrDgMCGgUAMAsGCSqGSIb3
DQEHAaCCA/4wggP6MIIC4qADAgECAhQoC/xUcLhcK13sGSiYxuUPf758tDANBgkq
hkiG9w0BAQsFADB8MQswCQYDVQQGEwJYWDESMBAGA1UEBwwJQWNtZSBDaXR5MR4w
HAYDVQQKDBVBY21lIENlcnRpZmljYXRlcyBMdGQxFDASBgNVBAMMC2V4YW1wbGUu
Y29tMSMwIQYJKoZIhvcNAQkBFhR2YWxpZC1jYUBleGFtcGxlLmNvbTAeFw0yMjEx
MDQxMjU1MzlaFw0zMjExMDExMjU1MzlaMHwxCzAJBgNVBAYTAlhYMRIwEAYDVQQH
DAlBY21lIENpdHkxHjAcBgNVBAoMFUFjbWUgQ2VydGlmaWNhdGVzIEx0ZDEUMBIG
A1UEAwwLZXhhbXBsZS5jb20xIzAhBgkqhkiG9w0BCQEWFHZhbGlkLWNhQGV4YW1w
bGUuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAzTyW1VRm+mQH
YU5hZWgzgrPRkRzMLlLoCamlRs5DjGf3zIpo9a/17m60YfIXBJruImUBXxa5lp0Z
qaayty+nZOpS0wBZSqTLuslZ0WuyyyW2DEBNO7jLW58cMn8MfAwMYjDcxtubNb7M
AG9iZRj6wn6tKCsXtYUgAIpNhyPPtDuEZ5df1ecOnvlW2vO+MwytM8DLLtwolET/
tPzOXPHDiyKjij02jyJ1DlZxptKudiKaBeY1WY/W/PpS6fGskTJQc/bZPgE3OP/9
Y9Y1uzZTulkO3R6MlLNdLam32/ehpJvSWyfSbToyC2ejvaXoRChPFcAmTpqA0HPg
h8sudiS14QIDAQABo3QwcjAdBgNVHQ4EFgQUR0gRYoOCPJYqFiPGwB141VTCXuIw
HwYDVR0jBBgwFoAUR0gRYoOCPJYqFiPGwB141VTCXuIwDwYDVR0TAQH/BAUwAwEB
/zAfBgNVHREEGDAWgRR2YWxpZC1jYUBleGFtcGxlLmNvbTANBgkqhkiG9w0BAQsF
AAOCAQEAJDQsNTWxVLNHBrVl6p6IetzeBt64GoWp6OP5C8/HY3dlznfbrn6ZvWBr
Vsnc7VZ49r8r/QvRKRrljkIQuNwaW/LmRxJ1AVGGiorspmrdz0Lf2WOXnLH/+4lR
q5dar5YmGqi9Mo2j5ALg/2MiO1PonuKs1eX9tLZCCeanXFexU0qaeFZelunJ6UUm
BXpaGO1QfECcNnvarudosbe1ve6YGABn8MpAY+8zdYnYHPB8Pojhzvk7/8PMHoPu
njDb3lZT+b8BDmNz+GISCUSHYkdK2rWh+8wqD3T5rOtzTqAPuSNeDNocUF6wzxem
HWqvP7yM3VoXYvn7FA4NCa9mE3k8MzGCApcwggKTAgEBMIGUMHwxCzAJBgNVBAYT
AlhYMRIwEAYDVQQHDAlBY21lIENpdHkxHjAcBgNVBAoMFUFjbWUgQ2VydGlmaWNh
dGVzIEx0ZDEUMBIGA1UEAwwLZXhhbXBsZS5jb20xIzAhBgkqhkiG9w0BCQEWFHZh
bGlkLWNhQGV4YW1wbGUuY29tAhQoC/xUcLhcK13sGSiYxuUPf758tDAJBgUrDgMC
GgUAoIHYMBgGCSqGSIb3DQEJAzELBgkqhkiG9w0BBwEwHAYJKoZIhvcNAQkFMQ8X
DTIyMTEwNDEzMzE1NVowIwYJKoZIhvcNAQkEMRYEFK+sB97I9ZGpDGUMWLsE3dCU
/pbtMHkGCSqGSIb3DQEJDzFsMGowCwYJYIZIAWUDBAEqMAsGCWCGSAFlAwQBFjAL
BglghkgBZQMEAQIwCgYIKoZIhvcNAwcwDgYIKoZIhvcNAwICAgCAMA0GCCqGSIb3
DQMCAgFAMAcGBSsOAwIHMA0GCCqGSIb3DQMCAgEoMA0GCSqGSIb3DQEBAQUABIIB
AFli04jnSd0d0lnE9GO23qzxI98QIbCz1NU1qk+zDyAhwOWQJdMUfuekk3g4Gn2q
OFzdvlIMpgG2W7AVZlLUQMQjWIoDWkTeqxM8n6StoXcDvlArBHQDbourufFUu7OE
3TVsI6l/jZG3Xub9Uar0S3lF6rQ/A3vl28poRL/EIQye6ypg6UbS/EvkvfbsKUJD
6SpExlwh0R7lk1g/xn3tFVSEAH7VSJYr/8C/Bak06NPOtZZSSeU9ryRzeK/gN3SJ
nrEp+NkTezzApjSnZasrPSbzmRL4+18x3kmAmnwR3aRi7F7KhCs78qPUEVP5XbhX
2hO9RjMy6Uki+R/AG4aempk=
------1FD9CF28CC0AD72EF1FF6D0511838F0E--

View File

@ -0,0 +1,67 @@
From: valid-ca@example.com
To: recipient@example.org
Subject: ACME: aDiFfErEnTtOkEn
Auto-Submitted: auto-generated; type=acme
Message-ID: <A2299BB.FF7788@example.org>
MIME-Version: 1.0
Content-Type: multipart/signed; protocol="application/x-pkcs7-signature"; micalg="sha1"; boundary="----6E9953AAECB0BDB6F65BCD88900D3E15"
This is an S/MIME signed message
------6E9953AAECB0BDB6F65BCD88900D3E15
Content-Type: message/RFC822; forwarded=no
From: valid-ca@example.com
To: recipient@example.org
Subject: ACME: LgYemJLy3F1LDkiJrdIGbEzyFJyOyf6vBdyZ1TG3sME=
Message-ID: <A2299BB.FF7788@example.org>
MIME-Version: 1.0
Content-Type: text/plain; charset=utf-8
This is an automatically generated ACME challenge.
------6E9953AAECB0BDB6F65BCD88900D3E15
Content-Type: application/x-pkcs7-signature; name="smime.p7s"
Content-Transfer-Encoding: base64
Content-Disposition: attachment; filename="smime.p7s"
MIIGzQYJKoZIhvcNAQcCoIIGvjCCBroCAQExCzAJBgUrDgMCGgUAMAsGCSqGSIb3
DQEHAaCCA/4wggP6MIIC4qADAgECAhQoC/xUcLhcK13sGSiYxuUPf758tDANBgkq
hkiG9w0BAQsFADB8MQswCQYDVQQGEwJYWDESMBAGA1UEBwwJQWNtZSBDaXR5MR4w
HAYDVQQKDBVBY21lIENlcnRpZmljYXRlcyBMdGQxFDASBgNVBAMMC2V4YW1wbGUu
Y29tMSMwIQYJKoZIhvcNAQkBFhR2YWxpZC1jYUBleGFtcGxlLmNvbTAeFw0yMjEx
MDQxMjU1MzlaFw0zMjExMDExMjU1MzlaMHwxCzAJBgNVBAYTAlhYMRIwEAYDVQQH
DAlBY21lIENpdHkxHjAcBgNVBAoMFUFjbWUgQ2VydGlmaWNhdGVzIEx0ZDEUMBIG
A1UEAwwLZXhhbXBsZS5jb20xIzAhBgkqhkiG9w0BCQEWFHZhbGlkLWNhQGV4YW1w
bGUuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAzTyW1VRm+mQH
YU5hZWgzgrPRkRzMLlLoCamlRs5DjGf3zIpo9a/17m60YfIXBJruImUBXxa5lp0Z
qaayty+nZOpS0wBZSqTLuslZ0WuyyyW2DEBNO7jLW58cMn8MfAwMYjDcxtubNb7M
AG9iZRj6wn6tKCsXtYUgAIpNhyPPtDuEZ5df1ecOnvlW2vO+MwytM8DLLtwolET/
tPzOXPHDiyKjij02jyJ1DlZxptKudiKaBeY1WY/W/PpS6fGskTJQc/bZPgE3OP/9
Y9Y1uzZTulkO3R6MlLNdLam32/ehpJvSWyfSbToyC2ejvaXoRChPFcAmTpqA0HPg
h8sudiS14QIDAQABo3QwcjAdBgNVHQ4EFgQUR0gRYoOCPJYqFiPGwB141VTCXuIw
HwYDVR0jBBgwFoAUR0gRYoOCPJYqFiPGwB141VTCXuIwDwYDVR0TAQH/BAUwAwEB
/zAfBgNVHREEGDAWgRR2YWxpZC1jYUBleGFtcGxlLmNvbTANBgkqhkiG9w0BAQsF
AAOCAQEAJDQsNTWxVLNHBrVl6p6IetzeBt64GoWp6OP5C8/HY3dlznfbrn6ZvWBr
Vsnc7VZ49r8r/QvRKRrljkIQuNwaW/LmRxJ1AVGGiorspmrdz0Lf2WOXnLH/+4lR
q5dar5YmGqi9Mo2j5ALg/2MiO1PonuKs1eX9tLZCCeanXFexU0qaeFZelunJ6UUm
BXpaGO1QfECcNnvarudosbe1ve6YGABn8MpAY+8zdYnYHPB8Pojhzvk7/8PMHoPu
njDb3lZT+b8BDmNz+GISCUSHYkdK2rWh+8wqD3T5rOtzTqAPuSNeDNocUF6wzxem
HWqvP7yM3VoXYvn7FA4NCa9mE3k8MzGCApcwggKTAgEBMIGUMHwxCzAJBgNVBAYT
AlhYMRIwEAYDVQQHDAlBY21lIENpdHkxHjAcBgNVBAoMFUFjbWUgQ2VydGlmaWNh
dGVzIEx0ZDEUMBIGA1UEAwwLZXhhbXBsZS5jb20xIzAhBgkqhkiG9w0BCQEWFHZh
bGlkLWNhQGV4YW1wbGUuY29tAhQoC/xUcLhcK13sGSiYxuUPf758tDAJBgUrDgMC
GgUAoIHYMBgGCSqGSIb3DQEJAzELBgkqhkiG9w0BBwEwHAYJKoZIhvcNAQkFMQ8X
DTIyMTEwNDEzMzE1NVowIwYJKoZIhvcNAQkEMRYEFK+sB97I9ZGpDGUMWLsE3dCU
/pbtMHkGCSqGSIb3DQEJDzFsMGowCwYJYIZIAWUDBAEqMAsGCWCGSAFlAwQBFjAL
BglghkgBZQMEAQIwCgYIKoZIhvcNAwcwDgYIKoZIhvcNAwICAgCAMA0GCCqGSIb3
DQMCAgFAMAcGBSsOAwIHMA0GCCqGSIb3DQMCAgEoMA0GCSqGSIb3DQEBAQUABIIB
AFli04jnSd0d0lnE9GO23qzxI98QIbCz1NU1qk+zDyAhwOWQJdMUfuekk3g4Gn2q
OFzdvlIMpgG2W7AVZlLUQMQjWIoDWkTeqxM8n6StoXcDvlArBHQDbourufFUu7OE
3TVsI6l/jZG3Xub9Uar0S3lF6rQ/A3vl28poRL/EIQye6ypg6UbS/EvkvfbsKUJD
6SpExlwh0R7lk1g/xn3tFVSEAH7VSJYr/8C/Bak06NPOtZZSSeU9ryRzeK/gN3SJ
nrEp+NkTezzApjSnZasrPSbzmRL4+18x3kmAmnwR3aRi7F7KhCs78qPUEVP5XbhX
2hO9RjMy6Uki+R/AG4aempk=
------6E9953AAECB0BDB6F65BCD88900D3E15--

View File

@ -0,0 +1,67 @@
From: valid-ca@example.com
To: tampered-recipient@example.com
Subject: ACME: LgYemJLy3F1LDkiJrdIGbEzyFJyOyf6vBdyZ1TG3sME=
Auto-Submitted: auto-generated; type=acme
Message-ID: <A2299BB.FF7788@example.org>
MIME-Version: 1.0
Content-Type: multipart/signed; protocol="application/x-pkcs7-signature"; micalg="sha1"; boundary="----2D5F3855936C8172B69EB7BC1C12A23A"
This is an S/MIME signed message
------2D5F3855936C8172B69EB7BC1C12A23A
Content-Type: message/RFC822; forwarded=no
From: valid-ca@example.com
To: recipient@example.org
Subject: ACME: LgYemJLy3F1LDkiJrdIGbEzyFJyOyf6vBdyZ1TG3sME=
Message-ID: <A2299BB.FF7788@example.org>
MIME-Version: 1.0
Content-Type: text/plain; charset=utf-8
This is an automatically generated ACME challenge.
------2D5F3855936C8172B69EB7BC1C12A23A
Content-Type: application/x-pkcs7-signature; name="smime.p7s"
Content-Transfer-Encoding: base64
Content-Disposition: attachment; filename="smime.p7s"
MIIGzQYJKoZIhvcNAQcCoIIGvjCCBroCAQExCzAJBgUrDgMCGgUAMAsGCSqGSIb3
DQEHAaCCA/4wggP6MIIC4qADAgECAhQoC/xUcLhcK13sGSiYxuUPf758tDANBgkq
hkiG9w0BAQsFADB8MQswCQYDVQQGEwJYWDESMBAGA1UEBwwJQWNtZSBDaXR5MR4w
HAYDVQQKDBVBY21lIENlcnRpZmljYXRlcyBMdGQxFDASBgNVBAMMC2V4YW1wbGUu
Y29tMSMwIQYJKoZIhvcNAQkBFhR2YWxpZC1jYUBleGFtcGxlLmNvbTAeFw0yMjEx
MDQxMjU1MzlaFw0zMjExMDExMjU1MzlaMHwxCzAJBgNVBAYTAlhYMRIwEAYDVQQH
DAlBY21lIENpdHkxHjAcBgNVBAoMFUFjbWUgQ2VydGlmaWNhdGVzIEx0ZDEUMBIG
A1UEAwwLZXhhbXBsZS5jb20xIzAhBgkqhkiG9w0BCQEWFHZhbGlkLWNhQGV4YW1w
bGUuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAzTyW1VRm+mQH
YU5hZWgzgrPRkRzMLlLoCamlRs5DjGf3zIpo9a/17m60YfIXBJruImUBXxa5lp0Z
qaayty+nZOpS0wBZSqTLuslZ0WuyyyW2DEBNO7jLW58cMn8MfAwMYjDcxtubNb7M
AG9iZRj6wn6tKCsXtYUgAIpNhyPPtDuEZ5df1ecOnvlW2vO+MwytM8DLLtwolET/
tPzOXPHDiyKjij02jyJ1DlZxptKudiKaBeY1WY/W/PpS6fGskTJQc/bZPgE3OP/9
Y9Y1uzZTulkO3R6MlLNdLam32/ehpJvSWyfSbToyC2ejvaXoRChPFcAmTpqA0HPg
h8sudiS14QIDAQABo3QwcjAdBgNVHQ4EFgQUR0gRYoOCPJYqFiPGwB141VTCXuIw
HwYDVR0jBBgwFoAUR0gRYoOCPJYqFiPGwB141VTCXuIwDwYDVR0TAQH/BAUwAwEB
/zAfBgNVHREEGDAWgRR2YWxpZC1jYUBleGFtcGxlLmNvbTANBgkqhkiG9w0BAQsF
AAOCAQEAJDQsNTWxVLNHBrVl6p6IetzeBt64GoWp6OP5C8/HY3dlznfbrn6ZvWBr
Vsnc7VZ49r8r/QvRKRrljkIQuNwaW/LmRxJ1AVGGiorspmrdz0Lf2WOXnLH/+4lR
q5dar5YmGqi9Mo2j5ALg/2MiO1PonuKs1eX9tLZCCeanXFexU0qaeFZelunJ6UUm
BXpaGO1QfECcNnvarudosbe1ve6YGABn8MpAY+8zdYnYHPB8Pojhzvk7/8PMHoPu
njDb3lZT+b8BDmNz+GISCUSHYkdK2rWh+8wqD3T5rOtzTqAPuSNeDNocUF6wzxem
HWqvP7yM3VoXYvn7FA4NCa9mE3k8MzGCApcwggKTAgEBMIGUMHwxCzAJBgNVBAYT
AlhYMRIwEAYDVQQHDAlBY21lIENpdHkxHjAcBgNVBAoMFUFjbWUgQ2VydGlmaWNh
dGVzIEx0ZDEUMBIGA1UEAwwLZXhhbXBsZS5jb20xIzAhBgkqhkiG9w0BCQEWFHZh
bGlkLWNhQGV4YW1wbGUuY29tAhQoC/xUcLhcK13sGSiYxuUPf758tDAJBgUrDgMC
GgUAoIHYMBgGCSqGSIb3DQEJAzELBgkqhkiG9w0BBwEwHAYJKoZIhvcNAQkFMQ8X
DTIyMTEwNDEzMzE1NVowIwYJKoZIhvcNAQkEMRYEFK+sB97I9ZGpDGUMWLsE3dCU
/pbtMHkGCSqGSIb3DQEJDzFsMGowCwYJYIZIAWUDBAEqMAsGCWCGSAFlAwQBFjAL
BglghkgBZQMEAQIwCgYIKoZIhvcNAwcwDgYIKoZIhvcNAwICAgCAMA0GCCqGSIb3
DQMCAgFAMAcGBSsOAwIHMA0GCCqGSIb3DQMCAgEoMA0GCSqGSIb3DQEBAQUABIIB
AFli04jnSd0d0lnE9GO23qzxI98QIbCz1NU1qk+zDyAhwOWQJdMUfuekk3g4Gn2q
OFzdvlIMpgG2W7AVZlLUQMQjWIoDWkTeqxM8n6StoXcDvlArBHQDbourufFUu7OE
3TVsI6l/jZG3Xub9Uar0S3lF6rQ/A3vl28poRL/EIQye6ypg6UbS/EvkvfbsKUJD
6SpExlwh0R7lk1g/xn3tFVSEAH7VSJYr/8C/Bak06NPOtZZSSeU9ryRzeK/gN3SJ
nrEp+NkTezzApjSnZasrPSbzmRL4+18x3kmAmnwR3aRi7F7KhCs78qPUEVP5XbhX
2hO9RjMy6Uki+R/AG4aempk=
------2D5F3855936C8172B69EB7BC1C12A23A--

View File

@ -0,0 +1,67 @@
From: valid-ca@example.com
To: recipient@example.org
Subject: ACME: LgYemJLy3F1LDkiJrdIGbEzyFJyOyf6vBdyZ1TG3sME=
Auto-Submitted: auto-generated; type=acme
Message-ID: <A2299BB.FF7788@example.org>
MIME-Version: 1.0
Content-Type: multipart/signed; protocol="application/x-pkcs7-signature"; micalg="sha1"; boundary="----2D188458DC295B22904B7A1FB62F57BF"
This is an S/MIME signed message
------2D188458DC295B22904B7A1FB62F57BF
Content-Type: message/RFC822; forwarded=no
From: valid-ca@example.com
To: recipient@example.org
Subject: ACME: LgYemJLy3F1LDkiJrdIGbEzyFJyOyf6vBdyZ1TG3sME=
Message-ID: <A2299BB.FF7788@example.org>
MIME-Version: 1.0
Content-Type: text/plain; charset=utf-8
This is an automatically generated ACME challenge.
------2D188458DC295B22904B7A1FB62F57BF
Content-Type: application/x-pkcs7-signature; name="smime.p7s"
Content-Transfer-Encoding: base64
Content-Disposition: attachment; filename="smime.p7s"
MIIG1QYJKoZIhvcNAQcCoIIGxjCCBsICAQExCzAJBgUrDgMCGgUAMAsGCSqGSIb3
DQEHAaCCBAQwggQAMIIC6KADAgECAhRuqrQwjQAEKPLiumz639inbqPeJzANBgkq
hkiG9w0BAQsFADB+MQswCQYDVQQGEwJYWDESMBAGA1UEBwwJQWNtZSBDaXR5MR4w
HAYDVQQKDBVFbWNhIENlcnRpZmljYXRlcyBMdGQxFDASBgNVBAMMC2V4YW1wbGUu
Y29tMSUwIwYJKoZIhvcNAQkBFhZpbnZhbGlkLWNhQGV4YW1wbGUuY29tMB4XDTIy
MTEwNDEyNTUzOVoXDTMyMTEwMTEyNTUzOVowfjELMAkGA1UEBhMCWFgxEjAQBgNV
BAcMCUFjbWUgQ2l0eTEeMBwGA1UECgwVRW1jYSBDZXJ0aWZpY2F0ZXMgTHRkMRQw
EgYDVQQDDAtleGFtcGxlLmNvbTElMCMGCSqGSIb3DQEJARYWaW52YWxpZC1jYUBl
eGFtcGxlLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMxVUSNR
pIreCKvtK148IWN7MWfK8fymmPOF8oQcFieCC0mfw8efc/4MEVA5qN3avHOXd1RG
VgaR+tM30zRiTLllc6YnPePUPZNSQmJcnXgMlRhmOeCfo2hNglWFBnP/CV29xarP
Cf94DqXGrLZ8L8uGtk/JsNOreced34V4RZ9WvN53HlyiNtEJJLggM17wzJZcV+rQ
7LtsBHZfDOdTScCpEDqLZDmLMVLEBUtrwo5+5mYw4M0PDEP2D4qPux7NAHuaG66F
Zt7mq6DcceG/AneuUN7xOyMQ9x/D3NfiSgXbZeJM+BbE0cT7EY9WdZBtsS6HjJA0
yt98FgAIDoSzRQcCAwEAAaN2MHQwHQYDVR0OBBYEFB0Xb1ErzzEjzPrJC8PvkAJ3
qnoGMB8GA1UdIwQYMBaAFB0Xb1ErzzEjzPrJC8PvkAJ3qnoGMA8GA1UdEwEB/wQF
MAMBAf8wIQYDVR0RBBowGIEWaW52YWxpZC1jYUBleGFtcGxlLmNvbTANBgkqhkiG
9w0BAQsFAAOCAQEAnwFh8H7lwMWrHSceM6MVt+5M0yVX/r6K5YWGI/AaFG2Q5jkz
6yIeESgiXukza4oKY1I1clZwWus9fnrwn+AWbtvKbGLklFWCUB60fx82dZwoO14Q
Tm3GX6wwC0Y5eFYiXwEJ4gnazBWEWscp4E94AKqr1EYuOI9sR22l/rNtANrEiVsT
P4+kUgLEr9Y5infYglXQMjDVfNXRSETBnx4a5Fljd7pSD4e7H19eMiByd78q98ze
t0g2anl2cJbdM6cgu5iyAgS3BgMrFMnd8m7KkZwum+tslNWA1tbDGK0AWH7ztjh3
orifkxq2Hw6tdypZoWoLrwSEDEvNIy8+sQjUOTGCApkwggKVAgEBMIGWMH4xCzAJ
BgNVBAYTAlhYMRIwEAYDVQQHDAlBY21lIENpdHkxHjAcBgNVBAoMFUVtY2EgQ2Vy
dGlmaWNhdGVzIEx0ZDEUMBIGA1UEAwwLZXhhbXBsZS5jb20xJTAjBgkqhkiG9w0B
CQEWFmludmFsaWQtY2FAZXhhbXBsZS5jb20CFG6qtDCNAAQo8uK6bPrf2Kduo94n
MAkGBSsOAwIaBQCggdgwGAYJKoZIhvcNAQkDMQsGCSqGSIb3DQEHATAcBgkqhkiG
9w0BCQUxDxcNMjIxMTA0MTMzMTU1WjAjBgkqhkiG9w0BCQQxFgQUr6wH3sj1kakM
ZQxYuwTd0JT+lu0weQYJKoZIhvcNAQkPMWwwajALBglghkgBZQMEASowCwYJYIZI
AWUDBAEWMAsGCWCGSAFlAwQBAjAKBggqhkiG9w0DBzAOBggqhkiG9w0DAgICAIAw
DQYIKoZIhvcNAwICAUAwBwYFKw4DAgcwDQYIKoZIhvcNAwICASgwDQYJKoZIhvcN
AQEBBQAEggEAdZ4XoWViOrqOd4kWZVD+12GXJ7NcCWHOiTdXZ0S/3TKwwTPwQ0f0
XZmk8iJwcfXqAv48ITK+yh4jk8urtrjS3xohHGxVonifbgxoLm/yHqA13D4F0M9q
r0jfmXAWfH1HDk6AtZA5c1IJVJNMcVcLHkib232FgpicgPEZNWvRr8zHCNN3dymF
Qme9h2BqHxy2+nX96BBEiRMImG9Z9G4+sOqpwiDTNgzr7nFtldKtjiV/GarBeteZ
nx4QcHXY307ydLDrh2JzLK/+LZEhTMBQ2rWGfAo/owDGi/Pal//SgqQWFy3luJp2
8WG34Z4CnG514YmRzN2Z1V3fvreRInllkQ==
------2D188458DC295B22904B7A1FB62F57BF--

View File

@ -0,0 +1,67 @@
From: valid-ca@example.com
To: recipient@example.org
Subject: ACME: LgYemJLy3F1LDkiJrdIGbEzyFJyOyf6vBdyZ1TG3sME=
Auto-Submitted: auto-generated; type=acme
Message-ID: <A2299BB.FF7788@example.org>
MIME-Version: 1.0
Content-Type: multipart/signed; protocol="application/x-pkcs7-signature"; micalg="sha1"; boundary="----163CF1BA3ECF9F288779BFBE9EF3E10C"
This is an S/MIME signed message
------163CF1BA3ECF9F288779BFBE9EF3E10C
Content-Type: message/RFC822; forwarded=no
From: valid-ca@example.com
To: recipient@example.org
Subject: ACME: LgYemJLy3F1LDkiJrdIGbEzyFJyOyf6vBdyZ1TG3sME=
Message-ID: <A2299BB.FF7788@example.org>
MIME-Version: 1.0
Content-Type: text/plain; charset=utf-8
This is an automatically generated ACME challenge.
------163CF1BA3ECF9F288779BFBE9EF3E10C
Content-Type: application/x-pkcs7-signature; name="smime.p7s"
Content-Transfer-Encoding: base64
Content-Disposition: attachment; filename="smime.p7s"
MIIGzQYJKoZIhvcNAQcCoIIGvjCCBroCAQExCzAJBgUrDgMCGgUAMAsGCSqGSIb3
DQEHAaCCA/4wggP6MIIC4qADAgECAhQoC/xUcLhcK13sGSiYxuUPf758tDANBgkq
hkiG9w0BAQsFADB8MQswCQYDVQQGEwJYWDESMBAGA1UEBwwJQWNtZSBDaXR5MR4w
HAYDVQQKDBVBY21lIENlcnRpZmljYXRlcyBMdGQxFDASBgNVBAMMC2V4YW1wbGUu
Y29tMSMwIQYJKoZIhvcNAQkBFhR2YWxpZC1jYUBleGFtcGxlLmNvbTAeFw0yMjEx
MDQxMjU1MzlaFw0zMjExMDExMjU1MzlaMHwxCzAJBgNVBAYTAlhYMRIwEAYDVQQH
DAlBY21lIENpdHkxHjAcBgNVBAoMFUFjbWUgQ2VydGlmaWNhdGVzIEx0ZDEUMBIG
A1UEAwwLZXhhbXBsZS5jb20xIzAhBgkqhkiG9w0BCQEWFHZhbGlkLWNhQGV4YW1w
bGUuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAzTyW1VRm+mQH
YU5hZWgzgrPRkRzMLlLoCamlRs5DjGf3zIpo9a/17m60YfIXBJruImUBXxa5lp0Z
qaayty+nZOpS0wBZSqTLuslZ0WuyyyW2DEBNO7jLW58cMn8MfAwMYjDcxtubNb7M
AG9iZRj6wn6tKCsXtYUgAIpNhyPPtDuEZ5df1ecOnvlW2vO+MwytM8DLLtwolET/
tPzOXPHDiyKjij02jyJ1DlZxptKudiKaBeY1WY/W/PpS6fGskTJQc/bZPgE3OP/9
Y9Y1uzZTulkO3R6MlLNdLam32/ehpJvSWyfSbToyC2ejvaXoRChPFcAmTpqA0HPg
h8sudiS14QIDAQABo3QwcjAdBgNVHQ4EFgQUR0gRYoOCPJYqFiPGwB141VTCXuIw
HwYDVR0jBBgwFoAUR0gRYoOCPJYqFiPGwB141VTCXuIwDwYDVR0TAQH/BAUwAwEB
/zAfBgNVHREEGDAWgRR2YWxpZC1jYUBleGFtcGxlLmNvbTANBgkqhkiG9w0BAQsF
AAOCAQEAJDQsNTWxVLNHBrVl6p6IetzeBt64GoWp6OP5C8/HY3dlznfbrn6ZvWBr
Vsnc7VZ49r8r/QvRKRrljkIQuNwaW/LmRxJ1AVGGiorspmrdz0Lf2WOXnLH/+4lR
q5dar5YmGqi9Mo2j5ALg/2MiO1PonuKs1eX9tLZCCeanXFexU0qaeFZelunJ6UUm
BXpaGO1QfECcNnvarudosbe1ve6YGABn8MpAY+8zdYnYHPB8Pojhzvk7/8PMHoPu
njDb3lZT+b8BDmNz+GISCUSHYkdK2rWh+8wqD3T5rOtzTqAPuSNeDNocUF6wzxem
HWqvP7yM3VoXYvn7FA4NCa9mE3k8MzGCApcwggKTAgEBMIGUMHwxCzAJBgNVBAYT
AlhYMRIwEAYDVQQHDAlBY21lIENpdHkxHjAcBgNVBAoMFUFjbWUgQ2VydGlmaWNh
dGVzIEx0ZDEUMBIGA1UEAwwLZXhhbXBsZS5jb20xIzAhBgkqhkiG9w0BCQEWFHZh
bGlkLWNhQGV4YW1wbGUuY29tAhQoC/xUcLhcK13sGSiYxuUPf758tDAJBgUrDgMC
GgUAoIHYMBgGCSqGSIb3DQEJAzELBgkqhkiG9w0BBwEwHAYJKoZIhvcNAQkFMQ8X
DTIyMTEwNDEzMzE1NVowIwYJKoZIhvcNAQkEMRYEFK+sB97I9ZGpDGUMWLsE3dCU
/pbtMHkGCSqGSIb3DQEJDzFsMGowCwYJYIZIAWUDBAEqMAsGCWCGSAFlAwQBFjAL
BglghkgBZQMEAQIwCgYIKoZIhvcNAwcwDgYIKoZIhvcNAwICAgCAMA0GCCqGSIb3
DQMCAgFAMAcGBSsOAwIHMA0GCCqGSIb3DQMCAgEoMA0GCSqGSIb3DQEBAQUABIIB
AFli04jnSd0d0lnE9GO23qzxI98QIbCz1NU1qk+zDyAhwOWQJdMUfuekk3g4Gn2q
OFzdvlIMpgG2W7AVZlLUQMQjWIoDWkTeqxM8n6StoXcDvlArBHQDbourufFUu7OE
3TVsI6l/jZG3Xub9Uar0S3lF6rQ/A3vl28poRL/EIQye6ypg6UbS/EvkvfbsKUJD
6SpExlwh0R7lk1g/xn3tFVSEAH7VSJYr/8C/Bak06NPOtZZSSeU9ryRzeK/gN3SJ
nrEp+NkTezzApjSnZasrPSbzmRL4+18x3kmAmnwR3aRi7F7KhCs78qPUEVP5XbhX
2hO9RjMy6Uki+R/AG4aempk=
------163CF1BA3ECF9F288779BFBE9EF3E10C--

View File

@ -0,0 +1,28 @@
-----BEGIN PRIVATE KEY-----
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDMVVEjUaSK3gir
7StePCFjezFnyvH8ppjzhfKEHBYnggtJn8PHn3P+DBFQOajd2rxzl3dURlYGkfrT
N9M0Yky5ZXOmJz3j1D2TUkJiXJ14DJUYZjngn6NoTYJVhQZz/wldvcWqzwn/eA6l
xqy2fC/LhrZPybDTq3nHnd+FeEWfVrzedx5cojbRCSS4IDNe8MyWXFfq0Oy7bAR2
XwznU0nAqRA6i2Q5izFSxAVLa8KOfuZmMODNDwxD9g+Kj7sezQB7mhuuhWbe5qug
3HHhvwJ3rlDe8TsjEPcfw9zX4koF22XiTPgWxNHE+xGPVnWQbbEuh4yQNMrffBYA
CA6Es0UHAgMBAAECggEAByliW6OL6dYYZbY9U+M1pF/3/lRNoPZR3A8wzdKSMDZN
oPn5ibCcByZzIOW0dnopKr//TbPdZgON0ANf4rEjUUguAn/Tmn2g3t3+N6ZZWpDO
VPmYQ7g0qP42eDreXAhvUprJJ9Bz4EFb+hF5kjfOEQsarrc5/GFBNm7hG7N4dTos
ANLHDVt9p++40mBC9UnFTI9fghYqqsBipI2hMcVtLlfsTyhfdYk2atC0YwWH2g7H
nyFsfrgwNlza5iAKuQn1vGhM6nigGBiDfHifNoTHZPhIcgHRwPvZWCpxcAt8wPw1
4blqZPRqmbOYQHisXVSbKJbgN8zZiZB2j/SSDv9IyQKBgQDqpiSaiBZ+bUfy5M07
mSClnHR8/zulR7NuSWZ5C92w2EFYI/OkSwKeshKdmafCz9VeVkeCUjjRkAGOF2pr
I1icdY0XDt/Pd+jQzDqTtiUwwMOLRyvV437nCsMer++LbKRblEL+uSLCPhV2dnyx
GUXj9JJhnGeZ+xKH20cE1dr2NQKBgQDe7QG6UGrXUzfN2BttvJfOAKETtVfl1Mm0
uEC2skPlY1KVFYOCMuOFXLHwlc0KYNzVh5qBH+3dPyX1zjTurXmnaOcW0LxgJ4JN
vKiXDLrt8HYJqANjYCh7vaZmuZ8RduR4iS+E+JG6JLAdfGIzPvzzUgbo5hXuFnQE
dN8gsJcFywKBgAF24fmY6dMGKZHJfcJmdT6zWELDcQLaDLOef6Y3vb1xzA6ZwtZ+
pViKMfWL1PExTNqW3UFh8/rS1D+nw8FBajcnwKapMBpiXDCZZbAwTdEdEttWqV5f
WhZlCcyyOmN7XRc5OKXQT/g4XPftS1/rkXUXvKYhTMA4QehZJPtRvlkVAoGBAMS+
h/fXYXQIjget4wdGmvPEumSad6jv09UbiIG1cxbQQeIxyo7uOr9IwAKFMyElu8D4
nPO5KkVJpkb6Ztz/XY7SlqEcOCTkuavCBUjKg2/b+VEsZ1EdXJ1ZE7M1v526QInh
CX9hobuXBZgAXuq7fKOCkXabGl+2kU4dl49SSvdhAoGAH06W9bZUiqkLIzv+LizW
rVP9fN4A8EGNtdWVrVD0Ql0hh/PBqGiiVwg47LAVuvpIc9kDDQM/5A6tF7aOGRDR
k5ZDIrnWmrGQcEMTcisc5OwPnNjLVzV9r0swmeWcqZDrfgxeKpG7vdRaQdR++0FU
1zaGc8HFGtTeJw6f6ZYttg8=
-----END PRIVATE KEY-----

View File

@ -0,0 +1,24 @@
-----BEGIN CERTIFICATE-----
MIIEADCCAuigAwIBAgIUbqq0MI0ABCjy4rps+t/Yp26j3icwDQYJKoZIhvcNAQEL
BQAwfjELMAkGA1UEBhMCWFgxEjAQBgNVBAcMCUFjbWUgQ2l0eTEeMBwGA1UECgwV
RW1jYSBDZXJ0aWZpY2F0ZXMgTHRkMRQwEgYDVQQDDAtleGFtcGxlLmNvbTElMCMG
CSqGSIb3DQEJARYWaW52YWxpZC1jYUBleGFtcGxlLmNvbTAeFw0yMjExMDQxMjU1
MzlaFw0zMjExMDExMjU1MzlaMH4xCzAJBgNVBAYTAlhYMRIwEAYDVQQHDAlBY21l
IENpdHkxHjAcBgNVBAoMFUVtY2EgQ2VydGlmaWNhdGVzIEx0ZDEUMBIGA1UEAwwL
ZXhhbXBsZS5jb20xJTAjBgkqhkiG9w0BCQEWFmludmFsaWQtY2FAZXhhbXBsZS5j
b20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDMVVEjUaSK3gir7Ste
PCFjezFnyvH8ppjzhfKEHBYnggtJn8PHn3P+DBFQOajd2rxzl3dURlYGkfrTN9M0
Yky5ZXOmJz3j1D2TUkJiXJ14DJUYZjngn6NoTYJVhQZz/wldvcWqzwn/eA6lxqy2
fC/LhrZPybDTq3nHnd+FeEWfVrzedx5cojbRCSS4IDNe8MyWXFfq0Oy7bAR2Xwzn
U0nAqRA6i2Q5izFSxAVLa8KOfuZmMODNDwxD9g+Kj7sezQB7mhuuhWbe5qug3HHh
vwJ3rlDe8TsjEPcfw9zX4koF22XiTPgWxNHE+xGPVnWQbbEuh4yQNMrffBYACA6E
s0UHAgMBAAGjdjB0MB0GA1UdDgQWBBQdF29RK88xI8z6yQvD75ACd6p6BjAfBgNV
HSMEGDAWgBQdF29RK88xI8z6yQvD75ACd6p6BjAPBgNVHRMBAf8EBTADAQH/MCEG
A1UdEQQaMBiBFmludmFsaWQtY2FAZXhhbXBsZS5jb20wDQYJKoZIhvcNAQELBQAD
ggEBAJ8BYfB+5cDFqx0nHjOjFbfuTNMlV/6+iuWFhiPwGhRtkOY5M+siHhEoIl7p
M2uKCmNSNXJWcFrrPX568J/gFm7bymxi5JRVglAetH8fNnWcKDteEE5txl+sMAtG
OXhWIl8BCeIJ2swVhFrHKeBPeACqq9RGLjiPbEdtpf6zbQDaxIlbEz+PpFICxK/W
OYp32IJV0DIw1XzV0UhEwZ8eGuRZY3e6Ug+Hux9fXjIgcne/KvfM3rdINmp5dnCW
3TOnILuYsgIEtwYDKxTJ3fJuypGcLpvrbJTVgNbWwxitAFh+87Y4d6K4n5Math8O
rXcqWaFqC68EhAxLzSMvPrEI1Dk=
-----END CERTIFICATE-----

View File

@ -0,0 +1,28 @@
-----BEGIN PRIVATE KEY-----
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDYEMlw6YXHpO5b
nyuk7gVdu8XtmhKkg6LhhZ9KYPstIdSr7EbH++h2KMVPVjprvijh35d5hMf9fiMU
71A93NkO4by3fOIUAJy2m1oXLBSOBhglAbI+Z+DeGfULPvari0Cp6A4TAvvw0DZh
wR3qzHS50AXzQLVAeL+QoEwOCP0A7I3Ihn+sE3wJlKR+Sk9hYj30WX8M65ievuQw
3VNRZLX3TqY+oGyWHVhXKnp90ZovoN2Uhj5kTlz0GFJP+37t4urt+cHkoH60O8cP
Wqcvznwr7keN9z0xRhPJ98OIlob3MeQQqLfN3QQZxp/Wv6WyRGoQg6pE6MtIOKZP
lDgK4cJhAgMBAAECggEAEZOKA4KnpJY84pyn5P6M1rNt9jZkolfoAd8IFnmdrS31
mje6CU4reqM1686IqZeaRUeWT6cWyr7+VRdjqGilCqIf4zBIRtbG6M7p7P0jveru
f2IsMQnrv72OUsggMlO9YqTzMiY5vvz9E4YtbBqOO0haF4/xvqkj8jyr+y9Nf4vY
Yag2EUddUX2Giqln97aE8G2O8+LJzXTIk1K15DfyBcq1kkye8OSkjsnLybP4wo+v
uRSDbReF//j+MDepP6IZNPBVyEDyjaXFrNmVb+USyHtzKXwl/GwoYjN7gtOtqc1V
8vVj1H6+RJK1E67ORLctzzMUZfG6vDB2UZ+vv0lqnQKBgQD7sYWfq1rMzUNhVX4D
ef0AOoQlBpnOiwoc+7JEXoZoKBw5xrTAxsKRtJFSgIYDYbfi2y3XLu5IO69nkao6
EqArzaGn+Uc5JcCw4GVVoe6q6W4SwTGC690d/D7R2h24CYjqrIXkFwtvvM6f+5gl
Vu8da1eXbH73JAEigkKtrvQqVQKBgQDbwzTL6E6hY6DXAxsWzSp1d1n/SK6NdByX
2yfCQRAQVX6KPVo8Gw1ZGH3IJ/KNVL/TXzDV3NfY6cYBbydcu0/EcVBS11+3KhT7
uB+Y6T3RThwzRDRBlaeJtUmN63JWmCOBiMIRPmJU2uxS3JzNMqC5QekkjRb/7S2J
300hAdlb3QKBgGkt3zRBTFmHcZ/sNRPI15RP38cFQiMQ8XH5MJ7njW1bTahLRF/G
76op9gyvDtG89TZE95wTzZm772ntcmCARhToAqUKQ9w6zZJcw5wMZotfrxMBTupy
HF4aejoB1yeAPIos/Gq7wpi4IvSyE/uOn7AAmoL54PjwP9Um8Cxaj0hdAoGBALQf
p6KJ4gj989LHxOhHeUmWbbmEBS4DwXvmMQxS76uzp2f/KXqiYappHI91zqRwllnV
Z92iiXhNA/Ig/Q5QqOzGQ6Piy50BbPl0zNE0O2rWrt6GRJ6M3ylL4eHk3W6EfHWr
dgVUMJyEY7b3A75chMfTchh3XCaga/bZhApNza4xAoGAZF1mFIowaUog23TWiann
ZuCrZObtd2bk4LdtUYSAs2oUFSEWOR4d/av+eb68yf+n69gk1MrP4BA3hyQOX1M9
Cz/9x/1Tewytywi36noxlgvTfAe3U93LxGjob4z2bsGQ0Xt8q4ngIrEvDeBQa0cq
bUOxUekM/49+AAToLiTrZhM=
-----END PRIVATE KEY-----

View File

@ -0,0 +1,23 @@
-----BEGIN CERTIFICATE-----
MIID2TCCAsGgAwIBAgIUT0oQs1tpYdA/PUXwyeZqLP4DACYwDQYJKoZIhvcNAQEL
BQAwfDELMAkGA1UEBhMCWFgxEjAQBgNVBAcMCUFjbWUgQ2l0eTEeMBwGA1UECgwV
QWNtZSBDZXJ0aWZpY2F0ZXMgTHRkMRQwEgYDVQQDDAtleGFtcGxlLmNvbTEjMCEG
CSqGSIb3DQEJARYUdmFsaWQtY2FAZXhhbXBsZS5jb20wHhcNMjIxMTA0MTI1NTM5
WhcNMzIxMTAxMTI1NTM5WjB8MQswCQYDVQQGEwJYWDESMBAGA1UEBwwJQWNtZSBD
aXR5MR4wHAYDVQQKDBVBY21lIENlcnRpZmljYXRlcyBMdGQxFDASBgNVBAMMC2V4
YW1wbGUuY29tMSMwIQYJKoZIhvcNAQkBFhR2YWxpZC1jYUBleGFtcGxlLmNvbTCC
ASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANgQyXDphcek7lufK6TuBV27
xe2aEqSDouGFn0pg+y0h1KvsRsf76HYoxU9WOmu+KOHfl3mEx/1+IxTvUD3c2Q7h
vLd84hQAnLabWhcsFI4GGCUBsj5n4N4Z9Qs+9quLQKnoDhMC+/DQNmHBHerMdLnQ
BfNAtUB4v5CgTA4I/QDsjciGf6wTfAmUpH5KT2FiPfRZfwzrmJ6+5DDdU1FktfdO
pj6gbJYdWFcqen3Rmi+g3ZSGPmROXPQYUk/7fu3i6u35weSgfrQ7xw9apy/OfCvu
R433PTFGE8n3w4iWhvcx5BCot83dBBnGn9a/pbJEahCDqkToy0g4pk+UOArhwmEC
AwEAAaNTMFEwHQYDVR0OBBYEFEaAxnUaGT+hDny105TcVjw00ZqfMB8GA1UdIwQY
MBaAFEaAxnUaGT+hDny105TcVjw00ZqfMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZI
hvcNAQELBQADggEBAL1SPWI/JS9HL3TWN0Q/xkP7UAhgQM6BUzgyVMJ+MxPy34l3
7vEsSYc3KY8ynNo/psbsBodDqq2iDthZKTtt/l9YAnQrfgpRP+pzLPFDKOB9cDGG
BoY5KC77B/TXuTfvdT7y0Bvey6C7KMvmPecLNt1sqlCdoOwOyLJN7+zCWVR9nfd4
Y27VkOvt/zz+cycLmxMIgwGL8nDg37wIoPftEASMvrhO18ZxISsRyabzd6fDZi8m
u1x97/djezOgoqMgasPBy+yOZbeRwamv1PVElTDJovb/rRS2WMne6+2nMOoQezA3
C5rRLtPAoKPPStt6E5tSQSWa42VBW2HGD/n4ros=
-----END CERTIFICATE-----

View File

@ -0,0 +1,28 @@
-----BEGIN PRIVATE KEY-----
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDNPJbVVGb6ZAdh
TmFlaDOCs9GRHMwuUugJqaVGzkOMZ/fMimj1r/XubrRh8hcEmu4iZQFfFrmWnRmp
prK3L6dk6lLTAFlKpMu6yVnRa7LLJbYMQE07uMtbnxwyfwx8DAxiMNzG25s1vswA
b2JlGPrCfq0oKxe1hSAAik2HI8+0O4Rnl1/V5w6e+Vba874zDK0zwMsu3CiURP+0
/M5c8cOLIqOKPTaPInUOVnGm0q52IpoF5jVZj9b8+lLp8ayRMlBz9tk+ATc4//1j
1jW7NlO6WQ7dHoyUs10tqbfb96Gkm9JbJ9JtOjILZ6O9pehEKE8VwCZOmoDQc+CH
yy52JLXhAgMBAAECggEAT/IIjSvV+zYou8o03TQEUKbr/Ls7e9X2pgDrrROes1wy
Zf4KWZ3Dzi9YW4jaV4RkO4idyqUHAPjMLM4O8pWA/qnaPm/12EIuS+Gv94gcus5D
Ri1sCFX4/QUTDkZ4Hf/xePQwo9Oad4qNW6QHr3rV/xoqKCn1D9O9/gfhoEEeYMVV
nPdHlRR1QPcbptsBGkMYrZ8LwjQb8rMvNTK8HRhc4cKrkobEFvwXcWNO9aIlLveP
Q58TlQUijeu4eZPM9c3vjl8rM7Oic/ftuU6jmt9IayAVoYblw7yzeEk3lnBlPzME
e01hr+QD6ZJiquO1stdm6a7TJv15lAghhTBysy7oawKBgQDcY+PBZByHOZ5GzDBb
fwcScm/RM5zMfDJKrYLNehawJ5egyUOeRm+Ym8d7PpCOWrHLURLpxak2Nrsmivf8
g1OYAFnWWQm6j3GmlnEspp65n/UVPHETq7tvqulOHmvcOVROsSgHWu8JMgk8mlK1
kEKlw3y5yzIB7SdyUhCjY5UMGwKBgQDuZeWfg44qvi9o3bb0zbjAHgh0hCQjHy/G
NVcaVXx06j0a1tAFyESmJqtCEbScrInZnrmpXmTvX5UQczWHi4RYMI8NNFmf3+i0
XHxWICFrQhdkxppvrEJeXbdfws+UJT8A9K8kokYRO3WyDzrngddfvCbYgHQjMly+
3Ke8oS2tswKBgQC0sf2ZoSA2ysn3mBCJ5AODX2pIZv3HNojxa4OUPuZ9NWj/fiS/
j1aOFCMg7DIPVVLytR1BqDtNZOBbAJPEaFRQivEdalEssdFn2W8fQdlfrkN+TtkT
XLlIHCQ/VXfvzt1Ny7hbF3Zm3qxuEMWBca8DQ91uY6gzpiKye5CCtfINQwKBgQDh
EPIoFlsxnzvDFQ6VL2MsfS4eUmKLhfXkepcxFWPaPQpTPFpIGzo0Ym1sgqqw/3Nl
MKS3cZZ5JxPj4+C1htH7MFzdan7yoMFhBa+c39itGkhbq+RBaa9+x5tHnPO8OS2y
CU8QluLvgeMrp5VE2yAqEcfavernD7TfvBHf04r8YQKBgCHRqnBwHOGHiWyy/Wdu
ySxqN1hfJWBdKRbtxTj5uCKp/orNtiRVB4GC1ZqhCJ38rZwMSSbp00o9nyDXlRfP
jTo2zHrxtwem5hB31cZalPt4twJ5SofmNLDngz7UxwpTs8jWaAf8yAtRgdxWbYRg
bRbZ+LceWQUl8T/RC7UQpy0Y
-----END PRIVATE KEY-----

View File

@ -0,0 +1,24 @@
-----BEGIN CERTIFICATE-----
MIID+jCCAuKgAwIBAgIUKAv8VHC4XCtd7BkomMblD3++fLQwDQYJKoZIhvcNAQEL
BQAwfDELMAkGA1UEBhMCWFgxEjAQBgNVBAcMCUFjbWUgQ2l0eTEeMBwGA1UECgwV
QWNtZSBDZXJ0aWZpY2F0ZXMgTHRkMRQwEgYDVQQDDAtleGFtcGxlLmNvbTEjMCEG
CSqGSIb3DQEJARYUdmFsaWQtY2FAZXhhbXBsZS5jb20wHhcNMjIxMTA0MTI1NTM5
WhcNMzIxMTAxMTI1NTM5WjB8MQswCQYDVQQGEwJYWDESMBAGA1UEBwwJQWNtZSBD
aXR5MR4wHAYDVQQKDBVBY21lIENlcnRpZmljYXRlcyBMdGQxFDASBgNVBAMMC2V4
YW1wbGUuY29tMSMwIQYJKoZIhvcNAQkBFhR2YWxpZC1jYUBleGFtcGxlLmNvbTCC
ASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAM08ltVUZvpkB2FOYWVoM4Kz
0ZEczC5S6AmppUbOQ4xn98yKaPWv9e5utGHyFwSa7iJlAV8WuZadGammsrcvp2Tq
UtMAWUqky7rJWdFrsssltgxATTu4y1ufHDJ/DHwMDGIw3MbbmzW+zABvYmUY+sJ+
rSgrF7WFIACKTYcjz7Q7hGeXX9XnDp75VtrzvjMMrTPAyy7cKJRE/7T8zlzxw4si
o4o9No8idQ5WcabSrnYimgXmNVmP1vz6UunxrJEyUHP22T4BNzj//WPWNbs2U7pZ
Dt0ejJSzXS2pt9v3oaSb0lsn0m06Mgtno72l6EQoTxXAJk6agNBz4IfLLnYkteEC
AwEAAaN0MHIwHQYDVR0OBBYEFEdIEWKDgjyWKhYjxsAdeNVUwl7iMB8GA1UdIwQY
MBaAFEdIEWKDgjyWKhYjxsAdeNVUwl7iMA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0R
BBgwFoEUdmFsaWQtY2FAZXhhbXBsZS5jb20wDQYJKoZIhvcNAQELBQADggEBACQ0
LDU1sVSzRwa1ZeqeiHrc3gbeuBqFqejj+QvPx2N3Zc53265+mb1ga1bJ3O1WePa/
K/0L0Ska5Y5CELjcGlvy5kcSdQFRhoqK7KZq3c9C39ljl5yx//uJUauXWq+WJhqo
vTKNo+QC4P9jIjtT6J7irNXl/bS2Qgnmp1xXsVNKmnhWXpbpyelFJgV6WhjtUHxA
nDZ72q7naLG3tb3umBgAZ/DKQGPvM3WJ2BzwfD6I4c75O//DzB6D7p4w295WU/m/
AQ5jc/hiEglEh2JHStq1ofvMKg90+azrc06gD7kjXgzaHFBesM8Xph1qrz+8jN1a
F2L5+xQODQmvZhN5PDM=
-----END CERTIFICATE-----

View File

@ -0,0 +1,118 @@
#!/bin/env python3
#
# acme4j - Java ACME client
#
# Copyright (C) 2022 Richard "Shred" Körber
# http://acme4j.shredzone.org
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
#
#
# This tool creates ACME test e-mails and signs them. It can be
# used to generate S/MIME mails for unit tests.
#
# Requires: M2Crypto
#
# WARNING: DO NOT USE THIS CODE TO GENERATE REAL S/MIME MAILS!
# This generator is only meant to create test mails for unit test
# purposes, and may lack security relevant features that are
# needed for real S/MIME mails.
#
from M2Crypto import BIO, Rand, SMIME
def makebuf(text):
return BIO.MemoryBuffer(bytes(text, 'UTF-8'))
def signmail(text, sender, recipient, subject, privkey, pubkey,
envelopeFrom=None, envelopeTo=None, envelopeSubject=None):
body = 'Content-Type: message/RFC822; forwarded=no\r\n\r\n'
body += 'From: {}\r\n'.format(sender)
body += 'To: {}\r\n'.format(recipient)
body += 'Subject: {}\r\n'.format(subject)
body += 'Message-ID: <A2299BB.FF7788@example.org>\r\n'
body += 'MIME-Version: 1.0\r\n'
body += 'Content-Type: text/plain; charset=utf-8\r\n'
body += '\r\n'
body += text
body += '\r\n'
s = SMIME.SMIME()
s.load_key(privkey, pubkey)
p7 = s.sign(makebuf(body), SMIME.PKCS7_DETACHED)
out = BIO.MemoryBuffer()
out.write('From: {}\r\n'.format(envelopeFrom if envelopeFrom is not None else sender))
out.write('To: {}\r\n'.format(envelopeTo if envelopeTo is not None else recipient))
out.write('Subject: {}\r\n'.format(envelopeSubject if envelopeSubject is not None else subject))
out.write('Auto-Submitted: auto-generated; type=acme\r\n')
out.write('Message-ID: <A2299BB.FF7788@example.org>\r\n')
s.write(out, p7, makebuf(body))
return out.read()
with open('src/test/resources/email/valid-mail.eml', 'wb') as w:
w.write(signmail('This is an automatically generated ACME challenge.',
'valid-ca@example.com',
'recipient@example.org',
'ACME: LgYemJLy3F1LDkiJrdIGbEzyFJyOyf6vBdyZ1TG3sME=',
'src/test/resources/valid-signer-privkey.pem',
'src/test/resources/valid-signer.pem'))
with open('src/test/resources/email/invalid-cert-mismatch.eml', 'wb') as w:
w.write(signmail('This is an automatically generated ACME challenge.',
'different-ca@example.com',
'recipient@example.org',
'ACME: LgYemJLy3F1LDkiJrdIGbEzyFJyOyf6vBdyZ1TG3sME=',
'src/test/resources/valid-signer-privkey.pem',
'src/test/resources/valid-signer.pem',
envelopeFrom="different-ca@example.org"))
with open('src/test/resources/email/invalid-nosan.eml', 'wb') as w:
w.write(signmail('This is an automatically generated ACME challenge.',
'valid-ca@example.com',
'recipient@example.org',
'ACME: LgYemJLy3F1LDkiJrdIGbEzyFJyOyf6vBdyZ1TG3sME=',
'src/test/resources/valid-signer-nosan-privkey.pem',
'src/test/resources/valid-signer-nosan.pem'))
with open('src/test/resources/email/invalid-signed-mail.eml', 'wb') as w:
w.write(signmail('This is an automatically generated ACME challenge.',
'valid-ca@example.com',
'recipient@example.org',
'ACME: LgYemJLy3F1LDkiJrdIGbEzyFJyOyf6vBdyZ1TG3sME=',
'src/test/resources/invalid-signer-privkey.pem',
'src/test/resources/invalid-signer.pem'))
with open('src/test/resources/email/invalid-protected-mail-from.eml', 'wb') as w:
w.write(signmail('This is an automatically generated ACME challenge.',
'valid-ca@example.com',
'recipient@example.org',
'ACME: LgYemJLy3F1LDkiJrdIGbEzyFJyOyf6vBdyZ1TG3sME=',
'src/test/resources/valid-signer-privkey.pem',
'src/test/resources/valid-signer.pem',
envelopeFrom="tampered-ca@example.org"))
with open('src/test/resources/email/invalid-protected-mail-to.eml', 'wb') as w:
w.write(signmail('This is an automatically generated ACME challenge.',
'valid-ca@example.com',
'recipient@example.org',
'ACME: LgYemJLy3F1LDkiJrdIGbEzyFJyOyf6vBdyZ1TG3sME=',
'src/test/resources/valid-signer-privkey.pem',
'src/test/resources/valid-signer.pem',
envelopeTo="tampered-recipient@example.com"))
with open('src/test/resources/email/invalid-protected-mail-subject.eml', 'wb') as w:
w.write(signmail('This is an automatically generated ACME challenge.',
'valid-ca@example.com',
'recipient@example.org',
'ACME: LgYemJLy3F1LDkiJrdIGbEzyFJyOyf6vBdyZ1TG3sME=',
'src/test/resources/valid-signer-privkey.pem',
'src/test/resources/valid-signer.pem',
envelopeSubject="ACME: aDiFfErEnTtOkEn"))

View File

@ -0,0 +1,37 @@
#!/bin/bash
#
# acme4j - Java ACME client
#
# Copyright (C) 2022 Richard "Shred" Körber
# http://acme4j.shredzone.org
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
#
#
# Generates test keys for S/MIME unit tests.
#
# WARNING: DO NOT USE THIS CODE FOR KEY GENERATION IN PRODUCTION
# ENVIRONMENTS!
#
TARGET=src/test/resources/
openssl req -x509 -newkey rsa:2048 -sha256 -nodes -days 3650 \
-keyout "$TARGET/valid-signer-privkey.pem" -out "$TARGET/valid-signer.pem" \
-subj "/C=XX/L=Acme City/O=Acme Certificates Ltd/CN=example.com/emailAddress=valid-ca@example.com" \
-addext "subjectAltName=email:valid-ca@example.com"
openssl req -x509 -newkey rsa:2048 -sha256 -nodes -days 3650 \
-keyout "$TARGET/valid-signer-nosan-privkey.pem" -out "$TARGET/valid-signer-nosan.pem" \
-subj "/C=XX/L=Acme City/O=Acme Certificates Ltd/CN=example.com/emailAddress=valid-ca@example.com"
openssl req -x509 -newkey rsa:2048 -sha256 -nodes -days 3650 \
-keyout "$TARGET/invalid-signer-privkey.pem" -out "$TARGET/invalid-signer.pem" \
-subj "/C=XX/L=Acme City/O=Emca Certificates Ltd/CN=example.com/emailAddress=invalid-ca@example.com" \
-addext "subjectAltName=email:invalid-ca@example.com"

View File

@ -14,7 +14,7 @@ To use the S/MIME support, you need to:
* add the `acme4j-smime` module to your list of dependencies
* make sure that `BouncyCastleProvider` is added as security provider
[RFC 8823](https://tools.ietf.org/html/rfc8823) requires that the DKIM or S/MIME signature of incoming mails _must_ be checked. Outgoing mails _must_ have a valid DKIM or S/MIME signature. This is out of the scope of `acme4j-smime`, but is usually performed by a MTA.
[RFC 8823](https://tools.ietf.org/html/rfc8823) requires that the DKIM or S/MIME signature of incoming mails _must_ be checked. Outgoing mails _must_ have a valid DKIM signature. Starting with v2.15, _acme4j_ is able to validate and sign S/MIME verification mails. DKIM is usually done by the MTA and thus out of the scope of `acme4j-smime`.
## Ordering
@ -59,7 +59,7 @@ EmailReply00Challenge challenge = // challenge that is requested by the C
EmailIdentifier identifier = // email address to get the S/MIME cert for
javax.mail.Session mailSession = // javax.mail session
Message response = new EmailProcessor(challengeMessage)
Message response = EmailProcessor.plainMessage(challengeMessage)
.expectedIdentifier(identifier)
.withChallenge(challenge)
.respond()
@ -69,4 +69,28 @@ Transport.send(response); // send response to the CA
challenge.trigger(); // trigger the challenge
```
The `EmailProcessor` and the related `ResponseGenerator` offer more methods for validating and for customizing the response email.
The `EmailProcessor` and the related `ResponseGenerator` offer more methods for validating and for customizing the response email, see [the autodocs](../acme4j-smime/apidocs/org.shredzone.acme4j.smime/module-summary.html).
## Validating S/MIME Challenge E-Mails
The `EmailProcessor` is able to validate challenge e-mails that were signed by the CA using S/MIME. To do so, invoke the processor like this:
```java
Message challengeMessage = // incoming challenge message from the CA
EmailReply00Challenge challenge = // challenge that is requested by the CA
EmailIdentifier identifier = // email address to get the S/MIME cert for
javax.mail.Session mailSession = // javax.mail session
X509Certificate signCert = // CA's signing certificate, for validation
boolean strict = // strict checks?
Message response = EmailProcessor.smimeMessage(challengeMessage, mailSession, signCert, strict)
.expectedIdentifier(identifier)
.withChallenge(challenge)
.respond()
.generateResponse(mailSession);
Transport.send(response); // send response to the CA
challenge.trigger(); // trigger the challenge
```
If `strict` is set to `true`, the S/MIME protected headers `From:`, `To:`, and `Subject:` inside the e-mail **must** match these headers of the wrapping `challengeMessage`. It is recommended to do strict checks. However, if the inbound MTA is changing the headers of the wrapping mail, this flag can be set to `false` instead. In this case, the wrapping headers are ignored, and only the protected headers are used for responding to the challenge.

View File

@ -31,7 +31,7 @@ Latest version: ![maven central](https://shredzone.org/maven-central/org.shredzo
* [jose4j](https://bitbucket.org/b_c/jose4j/wiki/Home)
* [slf4j](http://www.slf4j.org/)
* For `acme4j-utils`: [Bouncy Castle](https://www.bouncycastle.org/)
* For `acme4j-smime`: [Jakarta Mail](https://eclipse-ee4j.github.io/mail/)
* For `acme4j-smime`: [Jakarta Mail](https://eclipse-ee4j.github.io/mail/), [Bouncy Castle](https://www.bouncycastle.org/)
## Quick Start

View File

@ -2,6 +2,11 @@
This document will help you migrate your code to the latest _acme4j_ version.
## Migration to Version 2.15
- `acme4j-smime` requires BouncyCastle now. The `BouncyCastleProvider` must also be added as security provider.
- In `acme4j-smime`, the `EmailProcessor` constructor is private now. Use `EmailProcessor.plainMessage()` as drop-in replacement.
## Migration to Version 2.13
- The `acme4j-smime` module has switched from _JavaMail_ to _Jakarta Mail_. Unfortunately, this is a breaking API change because classes like `javax.mail.internet.InternetAddress` have moved to respective `jakarta.mail` packages.