Description
It is currently possible to create a NimbusJwtEncoder using an ImmutableJWKSet that contains an Ed25519 key (via OctetKeyPair), but it is not possible to use that encoder to generate a JWT signed with Ed25519 (EdDSA).
Problem Details
JwtEncoderParameters.from(header, claims)does not allow custom algorithms in the header.- Only enums implementing
JwsAlgorithmorJwaAlgorithmare allowed. - The default
SignatureAlgorithmenum implementsJwsAlgorithm, but does not include any EdDSA algorithms likeEdDSA(com.nimbusds.jose.JWSAlgorithm.EdDSA). - Even if a custom implementation of
JwsAlgorithmis provided to represent EdDSA, it does not resolve the problem because of howNimbusJwtEncoderinternally selects a signing key. - During encoding, the
selectJwkmethod is called. This uses aJWKSelectorcreated with a matcher generated by the privatecreateJwkMatchermethod. - Unfortunately,
createJwkMatcherdoes not support the EdDSA algorithm family, which results in the JWKSelector being constructed with a null matcher. - This causes
IllegalArgumentExceptionto be thrown at runtime due to the missing matcher.
When running following code:
val header = JwsHeader.with(FixedSignatureAlgorithm.Ed25519).build()
val claims = JwtClaimsSet.builder()
.issuer(issuer)
.issuedAt(Instant.now())
.expiresAt(expiresAt)
.claim(SESSION_ID_CLAIM_NAME, sessionId)
.build()
jwtEncoder.encode(JwtEncoderParameters.from(header, claims)).tokenValue
public enum FixedSignatureAlgorithm implements JwsAlgorithm {
RS256(JwsAlgorithms.RS256),
RS384(JwsAlgorithms.RS384),
RS512(JwsAlgorithms.RS512),
ES256(JwsAlgorithms.ES256),
ES384(JwsAlgorithms.ES384),
ES512(JwsAlgorithms.ES512),
PS256(JwsAlgorithms.PS256),
PS384(JwsAlgorithms.PS384),
PS512(JwsAlgorithms.PS512),
EdDSA(JWSAlgorithm.EdDSA.getName()),
Ed25519(JWSAlgorithm.Ed25519.getName());
private final String name;
FixedSignatureAlgorithm(String name) {
this.name = name;
}
@Override
public String getName() {
return this.name;
}
public static FixedSignatureAlgorithm from(String name) {
for (FixedSignatureAlgorithm value : values()) {
if (value.getName().equals(name)) {
return value;
}
}
return null;
}
}
then such error is thrown:
org.springframework.security.oauth2.jwt.JwtEncodingException: An error occurred while attempting to encode the Jwt: Failed to select a JWK signing key -> The JWK matcher must not be null
at org.springframework.security.oauth2.jwt.NimbusJwtEncoder.selectJwk(NimbusJwtEncoder.java:123)
at org.springframework.security.oauth2.jwt.NimbusJwtEncoder.encode(NimbusJwtEncoder.java:108)
Caused by: java.lang.IllegalArgumentException: The JWK matcher must not be null
at com.nimbusds.jose.jwk.JWKSelector.<init>(JWKSelector.java:51)
at org.springframework.security.oauth2.jwt.NimbusJwtEncoder.selectJwk(NimbusJwtEncoder.java:119)
... 158 common frames omitted
Suggested fix
To support Ed25519/EdDSA, the following would need to be addressed:
1. Add EdDSA (JWSAlgorithm.EdDSA) support to the SignatureAlgorithm enum or allow flexible/custom algorithm headers in JwtEncoderParameters.
2. Update createJwkMatcher in NimbusJwtEncoder to recognize and match EdDSA algorithm family keys like OctetKeyPair with Curve.Ed25519.
Used versions com.springframework.security:spring-security-oauth2-jose:6.3.3 com.nimbusds:nimbus-jose-jwt:9.40
Comment From: ClaudenirFreitas
Hi @jgrandja ,
I am facing the same incident: My application needs to verify the JWT signature using the EdDSA (Ed25519) algorithm, as specified by the issuer via the JWKS URI.
Used versions:
- spring-boot-starter-security: 3.5.3
- spring-boot-starter-oauth2-resource-server: 3.5.3
I would be happy to contribute to this if you do not mind.
Regards.
Comment From: jgrandja
@ClaudenirFreitas
We have a number of higher priority items on our list for the major release of Spring Security 7.0 and Spring Authorization Server 2.0 and with less resources on our team it will limit the new features in the next release.
We don't have the time to add this enhancement to the next release. I also prefer to wait on this to see if other users are in need of this enhancement before we actually schedule it for a release.