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

  1. Inspect the raw ACME authorization response and confirm the challenge token is a valid RFC 8555 Base64url string
  2. If testing against a mock ACME server, make it return properly formatted Base64url tokens
  3. Bypass or fix any proxy that may be modifying the ACME API responses
  4. 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

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

Related errors


AI-assisted analysis of quarkusio/quarkus@e1c734241f (2026-09-05). Data as JSON: /api/errors/c72b5927ba294b21. Report an issue: GitHub.