apache/pulsar · error · IOException
Failed to decode private key
Error message
Failed to decode private key
What it means
AuthTokenUtils.decodePrivateKey wraps any exception from building a PKCS8EncodedKeySpec and calling KeyFactory.generatePrivate (for the algorithm matching the JWT signature algorithm) into an IOException('Failed to decode private key'). It means the supplied byte[] is not a valid PKCS#8 encoded private key of the expected type.
Source
Thrown at pulsar-broker-common/src/main/java/org/apache/pulsar/broker/authentication/utils/AuthTokenUtils.java:64
@SuppressWarnings("deprecation")
@UtilityClass
public class AuthTokenUtils {
public static SecretKey createSecretKey(SignatureAlgorithm signatureAlgorithm) {
return Keys.secretKeyFor(signatureAlgorithm);
}
public static SecretKey decodeSecretKey(byte[] secretKey) {
return Keys.hmacShaKeyFor(secretKey);
}
public static PrivateKey decodePrivateKey(byte[] key, SignatureAlgorithm algType) throws IOException {
try {
PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(key);
KeyFactory kf = KeyFactory.getInstance(keyTypeForSignatureAlgorithm(algType));
return kf.generatePrivate(spec);
} catch (Exception e) {
throw new IOException("Failed to decode private key", e);
}
}
public static PublicKey decodePublicKey(byte[] key, SignatureAlgorithm algType) throws IOException {
try {
X509EncodedKeySpec spec = new X509EncodedKeySpec(key);
KeyFactory kf = KeyFactory.getInstance(keyTypeForSignatureAlgorithm(algType));
return kf.generatePublic(spec);
} catch (Exception e) {
throw new IOException("Failed to decode public key", e);
}
}
private static String keyTypeForSignatureAlgorithm(SignatureAlgorithm alg) {
if (alg.getFamilyName().equals("RSA")) {
return "RSA";
} else if (alg.getFamilyName().equals("ECDSA")) {View on GitHub (pinned to 820761864e)
Solutions
- Convert the key to PKCS#8: openssl pkcs8 -topk8 -nocrypt -in key.pem -out key.pkcs8.pem, then strip PEM armor and Base64-decode before calling
- Ensure the byte[] passed is the DER bytes, not the raw PEM text (decode Base64 body between BEGIN/END lines)
- Match algType to the key: RSA key for RS256/RS384/RS512, EC key for ES256/ES384/ES512
- Regenerate the key if corrupt/truncated and re-encode; inspect the wrapped cause 'e' for the exact crypto error
Example fix
// before
byte[] key = Files.readAllBytes(Paths.get("private.pem")); // PEM text, not DER
PrivateKey pk = AuthTokenUtils.decodePrivateKey(key, SignatureAlgorithm.RS256); // IOException
// after
String pem = Files.readString(Paths.get("private.pkcs8.pem"));
String b64 = pem.replace("-----BEGIN PRIVATE KEY-----", "")
.replace("-----END PRIVATE KEY-----", "").replaceAll("\\s", "");
PrivateKey pk = AuthTokenUtils.decodePrivateKey(Base64.getDecoder().decode(b64), SignatureAlgorithm.RS256); Defensive patterns
Strategy: validation
Validate before calling
// verify PKCS#8 DER before calling decodePrivateKey
static boolean isPkcs8(byte[] key) {
// DER SEQUENCE (0x30) with PKCS#8 PrivateKeyInfo version 0 INTEGER
return key != null && key.length > 2 && key[0] == 0x30
&& key.length > 6 && key[4] == 0x02 && key[5] == 0x01 && key[6] == 0x00;
} Try / catch
try {
PrivateKey pk = AuthTokenUtils.decodePrivateKey(keyBytes, alg);
} catch (IOException e) {
if (e.getMessage().equals("Failed to decode private key")) {
// inspect e.getCause(): InvalidKeySpecException/NoSuchAlgorithmException -> re-encode key to PKCS#8
}
throw new IllegalArgumentException("Key must be PKCS#8 DER for " + alg, e);
} Prevention
- Store and load private keys as PKCS#8 ('BEGIN PRIVATE KEY'), not PKCS#1 ('BEGIN RSA PRIVATE KEY')
- Always Base64-decode PEM bodies before passing byte[] to the decoder
- Match SignatureAlgorithm to key type (RSA vs EC)
- Log/inspect the wrapped cause for the precise crypto failure
- Validate keys at startup with a test decode instead of failing at runtime token generation
When it happens
Trigger: Calling decodePrivateKey with bytes that are not PKCS#8 DER (e.g. PEM text with headers, Base64 string not decoded, or PKCS#1 'RSA PRIVATE KEY' format); algorithm mismatch between algType and the key (e.g. EC key with RS256); truncated/corrupt key file.
Common situations: Passing the raw contents of a PEM file instead of stripping the header/footer and Base64-decoding; using an openssl key generated in 'BEGIN RSA PRIVATE KEY' (PKCS#1) form; copying the secret key string where an encoded private key is expected; wrong SignatureAlgorithm configured for the token provider.
Understand the failure class
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- Unable to find EC Parameter for the given curve oid: ${ecOID
- ${logCtx}Failed to load public key ${keyName}. ${e.getMessag
- The private key algorithm is not supported. attempted: ${fai
- ERROR_DECODING_JWT
- UNSUPPORTED_ALGORITHM
AI-assisted analysis of apache/pulsar@820761864e (2026-09-06).
Data as JSON: /api/errors/6ba79eae93a1d964.
Report an issue: GitHub.