Priority: Low — future improvement, not blocking any current release.
Background
The KMS already publishes EC public keys (P-256, P-384) via the JWKS endpoint
(GET /.well-known/jwks.json). Any JOSE-compliant client seeing those keys in
JWKS will naturally try to encrypt payloads using ECDH-ES or ECDH-ES+A128KW
(RFC 7518 §4.6) — the default key-management algorithm for EC keys in the web ecosystem.
Currently, the /v1/crypto/decrypt endpoint only handles dir and RSA-OAEP /
RSA-OAEP-256. This creates a gap: the JWKS endpoint advertises EC keys that the
decrypt endpoint cannot use for JWE decryption.
What ECDH-ES is
Ephemeral-static Diffie-Hellman key agreement. The sender:
- Generates a throwaway EC key pair (the
epk field in the JWE header).
- Performs DH against the recipient's static EC public key.
- Derives a CEK from the shared secret using Concat KDF (RFC 7518 Appendix C).
The KMS would only need the static private key — which never leaves the server.
Variants to support:
ECDH-ES — derived key is the CEK directly (no wrapping, enc must match key size)
ECDH-ES+A128KW — derived key wraps a random CEK with AES-128 Key Wrap
ECDH-ES+A256KW — same, with AES-256 Key Wrap
Proposed implementation scope
- Feature flag:
non-fips (start there; ECDH is SP 800-56A approved so FIPS
promotion is feasible in a follow-up once validated against the FIPS provider).
- Server-side pipeline (no key material leaves the server):
- Parse JWE protected header → extract
alg, enc, epk, apu, apv, kid
- KMIP
Get(kid) → static EC private key
EVP_PKEY_derive(static_privkey, epk) → shared_z
- Concat KDF(
shared_z, alg, apu, apv) → CEK or KEK
- For
+A128KW/+A256KW: AES-KW unwrap encrypted_key → CEK
- AES-GCM decrypt → plaintext
- New
JoseAlgorithm variants: EcdhEs, EcdhEsA128KW, EcdhEsA256KW
- Supported curves: P-256, P-384 (P-521 optional)
- Test vectors: RFC 7518 Appendix B + own KMS vectors
Why this matters
Without ECDH-ES decrypt, the JWKS-published EC keys are only usable for signature
verification. Clients that want to encrypt data to the KMS using its EC public key
have no supported path. RSA-OAEP remains the only encryption option, which forces
users to generate RSA keys instead of the more modern and compact EC keys.
Out of scope for this issue
- JWT issuance or OIDC
- ECDH-ES encrypt (the KMS is a decryption oracle; senders use the public key directly)
- X25519 / X448 (OKP key type) — separate issue if desired
Background
The KMS already publishes EC public keys (P-256, P-384) via the JWKS endpoint
(
GET /.well-known/jwks.json). Any JOSE-compliant client seeing those keys inJWKS will naturally try to encrypt payloads using
ECDH-ESorECDH-ES+A128KW(RFC 7518 §4.6) — the default key-management algorithm for EC keys in the web ecosystem.
Currently, the
/v1/crypto/decryptendpoint only handlesdirandRSA-OAEP/RSA-OAEP-256. This creates a gap: the JWKS endpoint advertises EC keys that thedecrypt endpoint cannot use for JWE decryption.
What ECDH-ES is
Ephemeral-static Diffie-Hellman key agreement. The sender:
epkfield in the JWE header).The KMS would only need the static private key — which never leaves the server.
Variants to support:
ECDH-ES— derived key is the CEK directly (no wrapping,encmust match key size)ECDH-ES+A128KW— derived key wraps a random CEK with AES-128 Key WrapECDH-ES+A256KW— same, with AES-256 Key WrapProposed implementation scope
non-fips(start there; ECDH is SP 800-56A approved so FIPSpromotion is feasible in a follow-up once validated against the FIPS provider).
alg,enc,epk,apu,apv,kidGet(kid)→ static EC private keyEVP_PKEY_derive(static_privkey, epk)→shared_zshared_z,alg,apu,apv) → CEK or KEK+A128KW/+A256KW: AES-KW unwrapencrypted_key→ CEKJoseAlgorithmvariants:EcdhEs,EcdhEsA128KW,EcdhEsA256KWWhy this matters
Without ECDH-ES decrypt, the JWKS-published EC keys are only usable for signature
verification. Clients that want to encrypt data to the KMS using its EC public key
have no supported path. RSA-OAEP remains the only encryption option, which forces
users to generate RSA keys instead of the more modern and compact EC keys.
Out of scope for this issue