{"record":{"id":"ef53d5d5cf1ec21f","repo":"justauth/JustAuth","slug":"failed-to-get-apple-private-key","errorCode":null,"errorMessage":"Failed to get apple private key","messagePattern":"Failed to get apple private key","errorType":"exception","errorClass":"AuthException","httpStatus":null,"severity":"error","filePath":"src/main/java/me/zhyd/oauth/request/AuthAppleRequest.java","lineNumber":137,"sourceCode":"            .issuer(this.config.getTeamId())\n            .subject(this.config.getClientId())\n            .audience().add(AUD).and()\n            .expiration(new Date(System.currentTimeMillis() + TimeUnit.MINUTES.toMillis(3)))\n            .issuedAt(new Date())\n            .signWith(getPrivateKey())\n            .compact();\n    }\n\n    private PrivateKey getPrivateKey() {\n        if (this.privateKey == null) {\n            synchronized (this) {\n                if (this.privateKey == null) {\n                    try (PEMParser pemParser = new PEMParser(new StringReader(this.config.getClientSecret()))) {\n                        JcaPEMKeyConverter pemKeyConverter = new JcaPEMKeyConverter();\n                        PrivateKeyInfo keyInfo = (PrivateKeyInfo) pemParser.readObject();\n                        this.privateKey = pemKeyConverter.getPrivateKey(keyInfo);\n                    } catch (IOException e) {\n                        throw new AuthException(\"Failed to get apple private key\", e);\n                    }\n                }\n            }\n        }\n        return this.privateKey;\n    }\n\n    @Data\n    static class AppleUserInfo {\n        private AppleUsername name;\n        private String email;\n    }\n\n    @Data\n    static class AppleUsername {\n        private String firstName;\n        private String lastName;\n    }","sourceCodeStart":119,"sourceCodeEnd":155,"githubUrl":"https://github.com/justauth/JustAuth/blob/694bbf1b010d93404e3bfb4824d90e9ddfaebebb/src/main/java/me/zhyd/oauth/request/AuthAppleRequest.java#L119-L155","documentation":"AuthException('Failed to get apple private key') thrown from AuthAppleRequest.getPrivateKey when parsing config.getClientSecret() as a PEM fails with IOException. The .p8 content is fed to BouncyCastle's PEMParser, cast to PrivateKeyInfo — anything that is not an EC private key in PKCS#8 PEM form triggers this (the IOException is chained).","triggerScenarios":"clientSecret containing a file path instead of PEM content, escaped \\n instead of real newlines, a truncated key, a wrong key type (RSA PEM or a public key), or BouncyCastle PEMParser missing from the classpath causing the try-with-resources to fail.","commonSituations":"Reading the .p8 from an env var where newlines were flattened; YAML/JSON config stripping or escaping the multiline PEM; pasting an APNs-style key without the BEGIN/END lines; shading/trimming dependencies so bcpkix is absent.","solutions":["Ensure clientSecret is the exact PEM text with real line breaks: -----BEGIN PRIVATE KEY----- ... -----END PRIVATE KEY-----","If loading from env/secret store, use mechanisms that preserve newlines (e.g. base64-encode then decode, or multiline YAML block scalars)","Confirm the bcpkix/bcprov (BouncyCastle) dependency is on the runtime classpath","Inspect the chained IOException message — 'recognised object type' or class-cast errors indicate wrong key material or PEM structure"],"exampleFix":"// before\nString key = System.getenv(\"APPLE_KEY\"); // flattened to one line with literal \\n\n// after\nString key = new String(Base64.getDecoder().decode(System.getenv(\"APPLE_KEY_B64\")), StandardCharsets.UTF_8);\n// key now contains real newlines and full PEM headers","handlingStrategy":"validation","validationCode":"String pem = config.getClientSecret();\nboolean looksLikePem = pem != null && pem.contains(\"-----BEGIN PRIVATE KEY-----\")\n    && pem.contains(\"\\n\") && pem.contains(\"-----END PRIVATE KEY-----\");\nif (!looksLikePem) {\n    throw new IllegalStateException(\"APPLE clientSecret is not a valid .p8 PEM (missing headers or newlines)\");\n}","typeGuard":"private static boolean isValidAppleP8(String pem) {\n    return pem != null\n        && pem.startsWith(\"-----BEGIN PRIVATE KEY-----\")\n        && pem.endsWith(\"-----END PRIVATE KEY-----\")\n        && pem.contains(\"\\n\");\n}","tryCatchPattern":"try {\n    AuthUser u = appleRequest.getUserInfo(token);\n} catch (AuthException e) {\n    if (\"Failed to get apple private key\".equals(e.getMessage())) {\n        // config bug, not runtime — check PEM format & BouncyCastle on classpath\n        throw new IllegalStateException(\"Apple .p8 config invalid; see cause\", e.getCause());\n    }\n    throw e;\n}","preventionTips":["Base64-encode PEM secrets in env vars and decode at runtime to preserve newlines","Verify bcpkix/bcprov are on the runtime classpath (esp. after fat-jar/shading)","Test config loading with an actual JWT mint before deploying"],"tags":["apple","pem","bouncycastle","config"],"backgroundTag":null,"analyzedSha":"694bbf1b010d93404e3bfb4824d90e9ddfaebebb","analyzedAt":"2026-08-14T15:16:59.945Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}