email-reply-00 Challenge¶
The email-reply-00
challenge permits to get end-user S/MIME certificates, as specified in RFC 8823.
The CA must support issuance of S/MIME certificates. Let's Encrypt does not currently support it.
Warning
The support of this challenge is experimental. The implementation is only unit tested for compliance with the RFC, but is not integration tested yet. There may be breaking changes in this part of the API in future releases.
Setup and Requirements¶
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 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¶
The certificate ordering process is similar to a standard domain certificate order.
However, if Identifier
objects are needed, use EmailIdentifier.email()
to generate an identifier for the email address you want an S/MIME certificate for.
To generate a CSR, the module provides a SMIMECSRBuilder
that works similar to the standard CSRBuilder
, but accepts EmailIdentifier
objects.
With the SMIMECSRBuilder.setKeyUsageType()
, the desired usage type of the S/MIME certificate can be selected. By default the certificate can be used both for encryption and signing. However this is just a proposal, and the CA is free to ignore it or return an error if the desired usage type is not supported.
Challenge and Response¶
The CA validates ownership of the email address by two components.
Firstly, the CA sends a challenge email to the email address that requested the S/MIME certificate. The subject of this email always starts with an ACME:
prefix, so it can be filtered by the inbound MTA for automatic processing. After the prefix, the mail subject contains the first part of the challenge token (called "Token 1").
Secondly, the CA provides a new EmailReply00Challenge
challenge that needs to be verified by the client. The challenge contains the second part of the challenge token (called "Token 2"). Both token parts are concatenated to give the full token that is required for generating the key authorization. The EmailReply00Challenge
class offers methods like getToken(String part1)
, getTokenPart2()
, and getAuthorization(String part1)
for that.
The client now needs to generate a response to the request email. This is a standard mail response to the sender's address. The subject line must be kept, except of an optional Re:
or a similar prefix. The mail body must contain a text/plain
part that contains the wrapped key authorization string. For example:
-----BEGIN ACME RESPONSE-----
LoqXcYV8q5ONbJQxbmR7SCTNo3tiAXDfowy
jxAjEuX0=
-----END ACME RESPONSE-----
This response is sent back to the CA.
After that, the EmailReply00Challenge
is triggered. The CA now has a proof of ownership of the email address, and can issue the S/MIME certificate.
Response Helper¶
The response process can be executed programatically, or even manually. To help with the process, acme4j-smime
provides an EmailProcessor
that helps you parsing the challenge email, and generate a matching response mail.
It is basically invoked like this:
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
Message response = EmailProcessor.plainMessage(challengeMessage)
.expectedIdentifier(identifier)
.withChallenge(challenge)
.respond()
.generateResponse(mailSession);
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, see the autodocs.
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:
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.