{"record":{"id":"6ba79eae93a1d964","repo":"apache/pulsar","slug":"failed-to-decode-private-key","errorCode":null,"errorMessage":"Failed to decode private key","messagePattern":"Failed to decode private key","errorType":"validation","errorClass":"IOException","httpStatus":null,"severity":"error","filePath":"pulsar-broker-common/src/main/java/org/apache/pulsar/broker/authentication/utils/AuthTokenUtils.java","lineNumber":64,"sourceCode":"@SuppressWarnings(\"deprecation\")\n@UtilityClass\npublic class AuthTokenUtils {\n\n    public static SecretKey createSecretKey(SignatureAlgorithm signatureAlgorithm) {\n        return Keys.secretKeyFor(signatureAlgorithm);\n    }\n\n    public static SecretKey decodeSecretKey(byte[] secretKey) {\n        return Keys.hmacShaKeyFor(secretKey);\n    }\n\n    public static PrivateKey decodePrivateKey(byte[] key, SignatureAlgorithm algType) throws IOException {\n        try {\n            PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(key);\n            KeyFactory kf = KeyFactory.getInstance(keyTypeForSignatureAlgorithm(algType));\n            return kf.generatePrivate(spec);\n        } catch (Exception e) {\n            throw new IOException(\"Failed to decode private key\", e);\n        }\n    }\n\n\n    public static PublicKey decodePublicKey(byte[] key, SignatureAlgorithm algType) throws IOException {\n        try {\n            X509EncodedKeySpec spec = new X509EncodedKeySpec(key);\n            KeyFactory kf = KeyFactory.getInstance(keyTypeForSignatureAlgorithm(algType));\n            return kf.generatePublic(spec);\n        } catch (Exception e) {\n            throw new IOException(\"Failed to decode public key\", e);\n        }\n    }\n\n    private static String keyTypeForSignatureAlgorithm(SignatureAlgorithm alg) {\n        if (alg.getFamilyName().equals(\"RSA\")) {\n            return \"RSA\";\n        } else if (alg.getFamilyName().equals(\"ECDSA\")) {","sourceCodeStart":46,"sourceCodeEnd":82,"githubUrl":"https://github.com/apache/pulsar/blob/820761864ed8e2a7d2e52dd9763ad2ae117c1395/pulsar-broker-common/src/main/java/org/apache/pulsar/broker/authentication/utils/AuthTokenUtils.java#L46-L82","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before\nbyte[] key = Files.readAllBytes(Paths.get(\"private.pem\")); // PEM text, not DER\nPrivateKey pk = AuthTokenUtils.decodePrivateKey(key, SignatureAlgorithm.RS256); // IOException\n// after\nString pem = Files.readString(Paths.get(\"private.pkcs8.pem\"));\nString b64 = pem.replace(\"-----BEGIN PRIVATE KEY-----\", \"\")\n    .replace(\"-----END PRIVATE KEY-----\", \"\").replaceAll(\"\\\\s\", \"\");\nPrivateKey pk = AuthTokenUtils.decodePrivateKey(Base64.getDecoder().decode(b64), SignatureAlgorithm.RS256);","handlingStrategy":"validation","validationCode":"// verify PKCS#8 DER before calling decodePrivateKey\nstatic boolean isPkcs8(byte[] key) {\n    // DER SEQUENCE (0x30) with PKCS#8 PrivateKeyInfo version 0 INTEGER\n    return key != null && key.length > 2 && key[0] == 0x30\n        && key.length > 6 && key[4] == 0x02 && key[5] == 0x01 && key[6] == 0x00;\n}","typeGuard":null,"tryCatchPattern":"try {\n    PrivateKey pk = AuthTokenUtils.decodePrivateKey(keyBytes, alg);\n} catch (IOException e) {\n    if (e.getMessage().equals(\"Failed to decode private key\")) {\n        // inspect e.getCause(): InvalidKeySpecException/NoSuchAlgorithmException -> re-encode key to PKCS#8\n    }\n    throw new IllegalArgumentException(\"Key must be PKCS#8 DER for \" + alg, e);\n}","preventionTips":["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"],"tags":["crypto","jwt","pem","pkcs8","keys"],"backgroundTag":"invalid-private-key","analyzedSha":"820761864ed8e2a7d2e52dd9763ad2ae117c1395","analyzedAt":"2026-09-06T00:14:20.138Z","contentChangedAt":"2026-09-06T00:14:20.138Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}