Add generator for DNS challenge resource name

pull/134/head
Richard Körber 2022-06-29 19:52:57 +02:00
parent 8d9ab54782
commit 379f184a41
No known key found for this signature in database
GPG Key ID: AAB9FD19C78AA3E0
8 changed files with 63 additions and 15 deletions

View File

@ -16,6 +16,7 @@ package org.shredzone.acme4j.challenge;
import static org.shredzone.acme4j.toolbox.AcmeUtils.base64UrlEncode; import static org.shredzone.acme4j.toolbox.AcmeUtils.base64UrlEncode;
import static org.shredzone.acme4j.toolbox.AcmeUtils.sha256hash; import static org.shredzone.acme4j.toolbox.AcmeUtils.sha256hash;
import org.shredzone.acme4j.Identifier;
import org.shredzone.acme4j.Login; import org.shredzone.acme4j.Login;
import org.shredzone.acme4j.toolbox.JSON; import org.shredzone.acme4j.toolbox.JSON;
@ -30,6 +31,39 @@ public class Dns01Challenge extends TokenChallenge {
*/ */
public static final String TYPE = "dns-01"; public static final String TYPE = "dns-01";
/**
* The prefix of the domain name to be used for the DNS TXT record.
*/
public static final String RECORD_NAME_PREFIX = "_acme-challenge";
/**
* Converts a domain identifier to the Resource Record name to be used for the DNS TXT
* record.
*
* @param identifier
* Domain {@link Identifier} of the domain to be validated
* @return Resource Record name (e.g. {@code _acme-challenge.www.example.org.}, note
* the trailing full stop character).
* @since 2.14
*/
public static String toRRName(Identifier identifier) {
return toRRName(identifier.getDomain());
}
/**
* Converts a domain identifier to the Resource Record name to be used for the DNS TXT
* record.
*
* @param domain
* Domain name to be validated
* @return Resource Record name (e.g. {@code _acme-challenge.www.example.org.}, note
* the trailing full stop character).
* @since 2.14
*/
public static String toRRName(String domain) {
return RECORD_NAME_PREFIX + '.' + domain + '.';
}
/** /**
* Creates a new generic {@link Dns01Challenge} object. * Creates a new generic {@link Dns01Challenge} object.
* *

View File

@ -15,11 +15,14 @@ package org.shredzone.acme4j.challenge;
import static net.javacrumbs.jsonunit.assertj.JsonAssertions.assertThatJson; import static net.javacrumbs.jsonunit.assertj.JsonAssertions.assertThatJson;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
import static org.shredzone.acme4j.toolbox.TestUtils.getJSON; import static org.shredzone.acme4j.toolbox.TestUtils.getJSON;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.shredzone.acme4j.Identifier;
import org.shredzone.acme4j.Login; import org.shredzone.acme4j.Login;
import org.shredzone.acme4j.Status; import org.shredzone.acme4j.Status;
import org.shredzone.acme4j.exception.AcmeProtocolException;
import org.shredzone.acme4j.toolbox.JSONBuilder; import org.shredzone.acme4j.toolbox.JSONBuilder;
import org.shredzone.acme4j.toolbox.TestUtils; import org.shredzone.acme4j.toolbox.TestUtils;
@ -48,4 +51,16 @@ public class DnsChallengeTest {
assertThatJson(response.toString()).isEqualTo("{}"); assertThatJson(response.toString()).isEqualTo("{}");
} }
@Test
public void testToRRName() {
assertThat(Dns01Challenge.toRRName("www.example.org"))
.isEqualTo("_acme-challenge.www.example.org.");
assertThat(Dns01Challenge.toRRName(Identifier.dns("www.example.org")))
.isEqualTo("_acme-challenge.www.example.org.");
assertThatExceptionOfType(AcmeProtocolException.class)
.isThrownBy(() -> Dns01Challenge.toRRName(Identifier.ip("127.0.0.10")));
assertThat(Dns01Challenge.RECORD_NAME_PREFIX)
.isEqualTo("_acme-challenge");
}
} }

View File

@ -362,15 +362,14 @@ public class ClientTest {
// Output the challenge, wait for acknowledge... // Output the challenge, wait for acknowledge...
LOG.info("Please create a TXT record:"); LOG.info("Please create a TXT record:");
LOG.info("_acme-challenge.{}. IN TXT {}", LOG.info("{} IN TXT {}",
auth.getIdentifier().getDomain(), challenge.getDigest()); Dns01Challenge.toRRName(auth.getIdentifier()), challenge.getDigest());
LOG.info("If you're ready, dismiss the dialog..."); LOG.info("If you're ready, dismiss the dialog...");
StringBuilder message = new StringBuilder(); StringBuilder message = new StringBuilder();
message.append("Please create a TXT record:\n\n"); message.append("Please create a TXT record:\n\n");
message.append("_acme-challenge.") message.append(Dns01Challenge.toRRName(auth.getIdentifier()))
.append(auth.getIdentifier().getDomain()) .append(" IN TXT ")
.append(". IN TXT ")
.append(challenge.getDigest()); .append(challenge.getDigest());
acceptChallenge(message.toString()); acceptChallenge(message.toString());

View File

@ -107,13 +107,13 @@ public class BammBammClient {
* another TXT Record is set, it will replace the existing one. * another TXT Record is set, it will replace the existing one.
* *
* @param domain * @param domain
* Domain to add the TXT Record to * Domain name to add the TXT Record to
* @param txt * @param txt
* TXT record to add * TXT record to add
*/ */
public void dnsAddTxtRecord(String domain, String txt) throws IOException { public void dnsAddTxtRecord(String domain, String txt) throws IOException {
JSONBuilder jb = new JSONBuilder(); JSONBuilder jb = new JSONBuilder();
jb.put("host", domain + '.'); jb.put("host", domain);
jb.put("value", txt); jb.put("value", txt);
sendRequest("set-txt", jb.toString()); sendRequest("set-txt", jb.toString());
} }

View File

@ -81,7 +81,7 @@ public class OrderIT extends PebbleITBase {
Dns01Challenge challenge = auth.findChallenge(Dns01Challenge.TYPE); Dns01Challenge challenge = auth.findChallenge(Dns01Challenge.TYPE);
assertThat(challenge).isNotNull(); assertThat(challenge).isNotNull();
String challengeDomainName = "_acme-challenge." + auth.getIdentifier().getDomain(); String challengeDomainName = Dns01Challenge.toRRName(auth.getIdentifier());
client.dnsAddTxtRecord(challengeDomainName, challenge.getDigest()); client.dnsAddTxtRecord(challengeDomainName, challenge.getDigest());

View File

@ -87,7 +87,7 @@ public class OrderWildcardIT extends PebbleITBase {
Dns01Challenge challenge = auth.findChallenge(Dns01Challenge.TYPE); Dns01Challenge challenge = auth.findChallenge(Dns01Challenge.TYPE);
assertThat(challenge).isNotNull(); assertThat(challenge).isNotNull();
String challengeDomainName = "_acme-challenge." + TEST_DOMAIN; String challengeDomainName = Dns01Challenge.toRRName(TEST_DOMAIN);
client.dnsAddTxtRecord(challengeDomainName, challenge.getDigest()); client.dnsAddTxtRecord(challengeDomainName, challenge.getDigest());
cleanup(() -> client.dnsRemoveTxtRecord(challengeDomainName)); cleanup(() -> client.dnsRemoveTxtRecord(challengeDomainName));

View File

@ -8,9 +8,10 @@ With the `dns-01` challenge, you prove to the CA that you are able to control th
Dns01Challenge challenge = auth.findChallenge(Dns01Challenge.class); Dns01Challenge challenge = auth.findChallenge(Dns01Challenge.class);
String domain = auth.getIdentifier().getDomain(); String domain = auth.getIdentifier().getDomain();
String resourceName = Dns01Challenge.toRRName(auth.getIdentifier());
String digest = challenge.getDigest(); String digest = challenge.getDigest();
``` ```
The CA expects a TXT record at `_acme-challenge.${domain}` with the `digest` string as value. The CA expects a TXT record at `_acme-challenge.${domain}.` with the `digest` string as value. The `Dns01Challenge.toRRName()` method converts the domain name to a resource record name (including the trailing full stop, e.g. `_acme-challenge.www.example.org.`). The `_acme-challenge` prefix is also available as constant (`Dns01Challenge.RECORD_NAME_PREFIX`).
The validation was successful if the CA was able to fetch the TXT record and got the correct `digest` returned. The validation was successful if the CA was able to fetch the TXT record and got the correct `digest` returned.

View File

@ -348,15 +348,14 @@ public Challenge dnsChallenge(Authorization auth) throws AcmeException {
// Output the challenge, wait for acknowledge... // Output the challenge, wait for acknowledge...
LOG.info("Please create a TXT record:"); LOG.info("Please create a TXT record:");
LOG.info("_acme-challenge.{}. IN TXT {}", LOG.info("{} IN TXT {}",
auth.getIdentifier().getDomain(), challenge.getDigest()); Dns01Challenge.toRRName(auth.getIdentifier()), challenge.getDigest());
LOG.info("If you're ready, dismiss the dialog..."); LOG.info("If you're ready, dismiss the dialog...");
StringBuilder message = new StringBuilder(); StringBuilder message = new StringBuilder();
message.append("Please create a TXT record:\n\n"); message.append("Please create a TXT record:\n\n");
message.append("_acme-challenge.") message.append(Dns01Challenge.toRRName(auth.getIdentifier()))
.append(auth.getIdentifier().getDomain()) .append(" IN TXT ")
.append(". IN TXT ")
.append(challenge.getDigest()); .append(challenge.getDigest());
acceptChallenge(message.toString()); acceptChallenge(message.toString());