ory/kratos · error
failed to decode PEM block containing private key
Error message
failed to decode PEM block containing private key
What it means
ProviderApple.newClientSecret decodes the configured Apple private key with pem.Decode and requires a PEM block of type 'PRIVATE KEY'. If the stored value is not valid PEM or the block type differs, it returns this error instead of proceeding to PKCS8 parsing. This happens when the Apple Sign-in .p8 key is not stored in proper PEM form in configuration.
Solutions
- Store the full PEM text including '-----BEGIN PRIVATE KEY-----' and '-----END PRIVATE KEY-----' lines
- When using an env var, keep newlines as \n escapes and ensure the config loader expands them, or base64-wrap it if supported
- Verify the block type is 'PRIVATE KEY' (PKCS8); convert PKCS1 'EC PRIVATE KEY' keys with: openssl pkcs8 -topk8 -nocrypt -in apple.p8
- Trim whitespace/newline mangling caused by YAML/env transport
Example fix
// before "private_key": "MIGTAgEAMBMGByqGSM49AgEGCCqGSM49AwEB..." // after "private_key": "-----BEGIN PRIVATE KEY-----\nMIGTAgEAMBMGByqGSM49AgEGCCqGSM49AwEB...\n-----END PRIVATE KEY-----"
Defensive patterns
Strategy: validation
Validate before calling
if !strings.Contains(privKey, "-----BEGIN PRIVATE KEY-----") { return errors.New("apple private_key must be PKCS8 PEM with BEGIN/END PRIVATE KEY armor") } Prevention
- Keep the .p8 key in PEM form end-to-end; never strip the armor lines
- Use \n escapes in env vars and ensure config expands them
- Validate at startup with pem.Decode before first OAuth call
- Convert non-PKCS8 keys with openssl pkcs8
When it happens
Trigger: Configuring the Apple OIDC provider with a private_key value that is raw base64/DER bytes without the BEGIN/END PEM armor, or a PEM block whose Type is not exactly 'PRIVATE KEY' (e.g. 'EC PRIVATE KEY').
Common situations: Admins paste the .p8 key content without the '-----BEGIN PRIVATE KEY-----' header; newline characters are stripped when the key is stored in an environment variable; an old pem-encoded key in a different format (PKCS1) is used.
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
- Private key is not ecdsa key
- no oidc provider was set
- Private key decoding failed
- Issuer URL must be set to autodiscover PKCE support
- no identifier found
AI-assisted analysis of ory/kratos@b86338da04 (2026-09-07).
Data as JSON: /api/errors/efb6ff8cb019ff0c.
Report an issue: GitHub.
Appendix: source
Thrown at selfservice/strategy/oidc/provider_apple.go:48
func NewProviderApple(
config *Configuration,
reg Dependencies,
) Provider {
config.IssuerURL = "https://appleid.apple.com"
return &ProviderApple{
ProviderGenericOIDC: &ProviderGenericOIDC{
config: config,
reg: reg,
},
JWKSUrl: "https://appleid.apple.com/auth/keys",
}
}
func (a *ProviderApple) newClientSecret() (string, error) {
// decode the pem format
block, _ := pem.Decode([]byte(a.config.PrivateKey))
if block == nil || block.Type != "PRIVATE KEY" {
return "", errors.New("failed to decode PEM block containing private key")
}
parsedKey, err := x509.ParsePKCS8PrivateKey(block.Bytes)
if err != nil {
return "", errors.Wrap(err, "Private key decoding failed")
}
privateKey, ok := parsedKey.(*ecdsa.PrivateKey)
if !ok {
return "", errors.New("Private key is not ecdsa key")
}
now := time.Now()
expirationTime := time.Now().Add(5 * time.Minute)
appleToken := jwt.NewWithClaims(jwt.SigningMethodES256,
jwt.RegisteredClaims{
Audience: []string{"https://appleid.apple.com"},
ExpiresAt: jwt.NewNumericDate(expirationTime),View on GitHub (pinned to b86338da04)