feat: allow ECDSA and ED25519 public keys (#11819)

# Which Problems Are Solved
Currently ZITADEL only allows the use of RSA public keys for machine
user authentication (jwt-bearer grant), including for system API users.
Attempting to use ECDSA (e.g. P-256) or ED25519 keys results in
`Errors.Internal` because `BytesToPublicKey` performs an
`ifc.(*rsa.PublicKey)` type assertion that returns `(nil, nil)` for
non-RSA keys, which then causes a nil key panic in go-jose during JWT
verification.

This is the same fix as #8433 (by @livio-a), rebased onto current
`main`.

# How the Problems Are Solved
- `BytesToPublicKey` now returns `crypto.PublicKey` (the standard
library interface) instead of `*rsa.PublicKey`
- A type switch validates the parsed key is one of `*rsa.PublicKey`,
`*ecdsa.PublicKey`, or `ed25519.PublicKey`
- A new `ErrNoPublicKey` sentinel error is returned for unsupported key
types instead of silently returning nil
- Callers in `system_token.go` and `query/key.go` are updated to use the
generic `crypto.PublicKey` interface

# Additional Changes
None

# Additional Context
Duplicate of #8433 which has been open since August 2024. We hit this
bug while implementing OIDC bootstrap for an SGX enclave that generates
ECDSA P-256 keys at runtime -- the `AddKey` API accepts the ECDSA SPKI
PEM fine, but the subsequent `jwt-bearer` token exchange fails with
`Errors.Internal` due to the nil key.

---------

Co-authored-by: Copilot <[email protected]>
Co-authored-by: Wim Van Laer <[email protected]>
This commit is contained in:
Bertrand
2026-04-09 11:30:23 +02:00
committed by GitHub
co-authored by Copilot Wim Van Laer
parent 9135ff4b36
commit b558a1f79f
4 changed files with 132 additions and 14 deletions
+5 -5
View File
@@ -3,21 +3,21 @@ package query
import (
"time"
"github.com/zitadel/zitadel/internal/crypto"
zcrypto "github.com/zitadel/zitadel/internal/crypto"
"github.com/zitadel/zitadel/internal/query/projection"
)
type Key interface {
ID() string
Algorithm() string
Use() crypto.KeyUsage
Use() zcrypto.KeyUsage
Sequence() uint64
}
type PrivateKey interface {
Key
Expiry() time.Time
Key() *crypto.CryptoValue
Key() *zcrypto.CryptoValue
}
type PublicKey interface {
@@ -38,7 +38,7 @@ type key struct {
sequence uint64
resourceOwner string
algorithm string
use crypto.KeyUsage
use zcrypto.KeyUsage
}
func (k *key) ID() string {
@@ -49,7 +49,7 @@ func (k *key) Algorithm() string {
return k.algorithm
}
func (k *key) Use() crypto.KeyUsage {
func (k *key) Use() zcrypto.KeyUsage {
return k.use
}