ory/kratos · error

Private key is not ecdsa key

Error message

Private key is not ecdsa key

What it means

After PEM decoding, newClientSecret parses the DER bytes with x509.ParsePKCS8PrivateKey and asserts the result is an *ecdsa.PrivateKey, because Apple client-secret JWTs are signed with ES256. If the PKCS8 payload decodes to any other key type (RSA, Ed25519), this error is returned.

Solutions

  1. Use Apple's ES256 (.p8) ECDSA private key downloaded from the Apple developer portal
  2. Verify with: openssl pkey -in apple.p8 -text -noout (should say 'Private-Key: (256 bit)' with ASN1 OID prime256v1)
  3. Generate a fresh .p8 key in Apple Developer console if the current one is not ECDSA

Example fix

// before
"apple": {"private_key": "<rsa pkcs8 pem>"}
// after
"apple": {"private_key": "-----BEGIN PRIVATE KEY-----\n<base64 EC PRIVATE KEY (prime256v1)>\n-----END PRIVATE KEY-----"}
Defensive patterns

Strategy: validation

Validate before calling

b, _ := pem.Decode([]byte(privKey)); k, err := x509.ParsePKCS8PrivateKey(b.Bytes); if _, ok := k.(*ecdsa.PrivateKey); !ok { return errors.New("apple key must be ECDSA P-256") }

Prevention

When it happens

Trigger: The Apple provider's private key is a valid PKCS8 PEM block but contains a non-ECDSA key (e.g. an RSA key from another provider), so the type assertion parsedKey.(*ecdsa.PrivateKey) fails.

Common situations: Wrong key pasted from a different OIDC provider; a regenerated Apple key in an unexpected algorithm; test fixtures using RSA keys.

Related errors


AI-assisted analysis of ory/kratos@b86338da04 (2026-09-07). Data as JSON: /api/errors/7bd994c96bf01f61. Report an issue: GitHub.

Appendix: source

Thrown at selfservice/strategy/oidc/provider_apple.go:57

		},
		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),
			IssuedAt:  jwt.NewNumericDate(now),
			Issuer:    a.config.TeamId,
			Subject:   a.config.ClientID,
		})
	appleToken.Header["kid"] = a.config.PrivateKeyId

	return appleToken.SignedString(privateKey)
}

View on GitHub (pinned to b86338da04)