quarkusio/quarkus · error · RuntimeException
Invalid certificate authority challenge
Error message
Invalid certificate authority challenge
What it means
After selecting the HTTP-01 challenge, AcmeClient validates the challenge token against TOKEN_REGEX before using it (the token later becomes part of the key authorization served on the challenge endpoint). If selectedChallenge.getToken() does not match the expected token format, the client rejects it with this RuntimeException rather than uploading untrusted/malformed content.
Source
Thrown at extensions/tls-registry/cli/src/main/java/io/quarkus/tls/cli/letsencrypt/AcmeClient.java:160
Assert.checkNotNullParam("challenges", challenges);
AcmeChallenge selectedChallenge = null;
for (AcmeChallenge challenge : challenges) {
if (challenge.getType() == AcmeChallenge.Type.HTTP_01) {
AUDIT.info("Selected HTTP-01 challenge for domain validation");
LOGGER.debug("HTTP 01 challenge is selected");
selectedChallenge = challenge;
break;
}
}
if (selectedChallenge == null) {
throw new RuntimeException("Missing certificate authority challenge");
}
// ensure the token is valid before proceeding
String token = selectedChallenge.getToken();
if (!token.matches(TOKEN_REGEX)) {
AUDIT.error("Invalid challenge token format - rejecting");
throw new RuntimeException("Invalid certificate authority challenge");
}
LOGGER.debugf("Preparing a selected challenge content for token %s", token);
String selectedChallengeString = selectedChallenge.getKeyAuthorization(account);
// Check rate limit before uploading challenge
checkRateLimit("challenge-upload");
// respond to the http challenge
if (managementClient != null) {
//TODO: Use JsonObject once POST is supported
//JsonObject challenge = new JsonObject().put("challenge-resource", token).put("challenge-content",
// selectedChallengeString);
HttpRequest<Buffer> request = managementClient.getAbs(challengeUrl);
request.addQueryParam("challenge-resource", token).addQueryParam("challenge-content", selectedChallengeString);
addKeyAndUser(request);
AUDIT.info("Uploading challenge to management endpoint - token: " + token.substring(0, Math.min(8, token.length()))
+ "..., endpoint: " + challengeUrl);View on GitHub (pinned to e1c734241f)
Solutions
- Inspect the raw ACME authorization response and confirm the challenge token is a valid RFC 8555 Base64url string
- If testing against a mock ACME server, make it return properly formatted Base64url tokens
- Bypass or fix any proxy that may be modifying the ACME API responses
- Verify you are on a current CA/library version; compare the token against the character set expected by TOKEN_REGEX
Example fix
// before: mock server returns a malformed token -> rejected
mockAcme.setChallengeToken("not a valid token!");
// after: RFC 8555-compliant Base64url token
mockAcme.setChallengeToken(Base64.getUrlEncoder().withoutPadding()
.encodeToString(randomBytes(32))); Defensive patterns
Strategy: validation
Validate before calling
import java.util.regex.Pattern;
private static final Pattern TOKEN_REGEX = Pattern.compile("^[A-Za-z0-9_-]+$");
static boolean isValidAcmeToken(String token) {
return token != null && !token.isEmpty()
&& TOKEN_REGEX.matcher(token).matches()
&& token.length() >= 32; // RFC 8555 tokens are >=128 bits Base64url
}
// reject malformed tokens before sending them to AcmeClient
Type guard
static boolean isBase64urlToken(String s) {
return s != null && s.matches("[A-Za-z0-9_-]{32,}");
}
Try / catch
try {
acmeClient.proveIdentifierControl(identifier, account, ...);
} catch (RuntimeException e) {
if (e.getMessage().equals("Invalid certificate authority challenge")) {
// discard the authorization, re-fetch order, and treat CA response as untrusted
} else {
throw e;
}
} Prevention
- Use real/staging ACME servers or mocks that emit RFC 8555-compliant Base64url tokens
- Inspect raw authorization JSON when tokens are rejected to spot proxy corruption
- Keep ACME client libraries up to date to track token format expectations
- Treat token validation failures as a security signal — audit the endpoint that produced the token
When it happens
Trigger: Calling proveIdentifierControl when the token returned by the CA for the selected HTTP-01 challenge fails the TOKEN_REGEX match — e.g. a compromised or misbehaving ACME endpoint returning malformed tokens, an unexpected/empty token field, or a proxy/mock ACME server that does not produce standard Base64url tokens.
Common situations: Testing against a fake/mock ACME server that returns dummy tokens; a middlebox or corporate proxy corrupting the ACME response; a CA implementation bug or protocol change producing tokens outside the RFC 8555 expected character set (Base64url); deserialization differences after upgrading the ACME library.
Understand the failure class
- SSL/TLS and certificate errors — how TLS handshakes and certificate validation fail.
Related errors
- Rate limit exceeded: too many ACME challenge requests. Wait
- Missing certificate authority challenge
- Failed to respond to certificate authority challenge
- Failure to save the account
- Failed to obtain certificate: <reason>
AI-assisted analysis of quarkusio/quarkus@e1c734241f (2026-09-05).
Data as JSON: /api/errors/c72b5927ba294b21.
Report an issue: GitHub.