apache/pulsar · error · AuthenticationException

Invalid signature

Error message

Invalid signature

What it means

After locating the signature suffix in a signed string, verifyAndExtract recomputes the HMAC over the payload with its configured secret and compares it to the received signature. If they differ the string was signed with a different secret or modified in transit, so an AuthenticationException "Invalid signature" is thrown. The comparison uses MessageDigest.isEqual (constant time) to avoid timing attacks.

Solutions

  1. Verify all broker nodes share the identical secretKey for SaslRoleTokenSigner
  2. Re-issue the token: re-authenticate so the client gets a token signed with the current secret
  3. If tampering is suspected, treat it as a security event and reject the request

Example fix

// server config: keep the same secret everywhere
// broker.conf (each node)
// saslRoleTokenSignerSecret=shared-hmac-secret
String role;
try {
    role = signer.verifyAndExtract(signedToken);
} catch (AuthenticationException e) {
    // force client re-authentication
    throw new RestException(Response.Status.UNAUTHORIZED, "re-authenticate");
}
Defensive patterns

Strategy: try-catch

Try / catch

try {
    String role = signer.verifyAndExtract(signedStr);
} catch (AuthenticationException e) {
    audit.log("signature mismatch for " + clientAddr); // security signal
    respond(401);
}

Prevention

When it happens

Trigger: verifyAndExtract() receives a well-formed signed string whose signature does not match a recomputation with the current secret key — typically after a broker secret-key change/rotation, or the payload was tampered with.

Common situations: Multiple brokers with different SaslRoleTokenSigner secret keys behind a load balancer; secretKey config changed on one node but not others; token copied between clusters; MITM tampering.

Related errors


AI-assisted analysis of apache/pulsar@820761864e (2026-09-06). Data as JSON: /api/errors/4c73d2854e1ac895. Report an issue: GitHub.

Appendix: source

Thrown at pulsar-broker-auth-sasl/src/main/java/org/apache/pulsar/broker/authentication/SaslRoleTokenSigner.java:80

    /**
     * Verifies a signed string and extracts the original string.
     *
     * @param signedStr the signed string to verify and extract.
     *
     * @return the extracted original string.
     *
     * @throws AuthenticationException thrown if the given string is not a signed string or if the signature is invalid.
     */
    public String verifyAndExtract(String signedStr) throws AuthenticationException {
        int index = signedStr.lastIndexOf(SIGNATURE);
        if (index == -1) {
            throw new AuthenticationException("Invalid signed text: " + signedStr);
        }
        String originalSignature = signedStr.substring(index + SIGNATURE.length());
        String rawValue = signedStr.substring(0, index);
        String currentSignature = computeSignature(rawValue);
        if (!MessageDigest.isEqual(originalSignature.getBytes(), currentSignature.getBytes())){
            throw new AuthenticationException("Invalid signature");
        }
        return rawValue;
    }

    /**
     * Returns the signature of a string.
     *
     * @param str string to sign.
     *
     * @return the signature for the string.
     */
    protected String computeSignature(String str) {
        try {
            MessageDigest md = MessageDigest.getInstance("SHA-512");

            md.update(str.getBytes());

            md.update(secret);

View on GitHub (pinned to 820761864e)