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.

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)

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.