From 462e38c0e36c57f073e68ba4bbfae1ff0fa5606e Mon Sep 17 00:00:00 2001 From: David Kowis Date: Thu, 8 May 2025 12:35:59 -0500 Subject: [PATCH] Fix DPoP jkt claim to be JWK SHA-256 thumbprint Just used the nimbus JOSE library to do it, because it already has a compliant implementation. Closes gh-17080 Signed-off-by: David Kowis --- .../DPoPAuthenticationProvider.java | 11 ++++------ .../DPoPAuthenticationProviderTests.java | 20 +++++++------------ 2 files changed, 11 insertions(+), 20 deletions(-) diff --git a/oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/authentication/DPoPAuthenticationProvider.java b/oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/authentication/DPoPAuthenticationProvider.java index 32d7b09fa1..aa98e6c2f5 100644 --- a/oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/authentication/DPoPAuthenticationProvider.java +++ b/oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/authentication/DPoPAuthenticationProvider.java @@ -210,25 +210,22 @@ public final class DPoPAuthenticationProvider implements AuthenticationProvider return OAuth2TokenValidatorResult.failure(error); } - PublicKey publicKey = null; + JWK jwk = null; @SuppressWarnings("unchecked") Map jwkJson = (Map) jwt.getHeaders().get("jwk"); try { - JWK jwk = JWK.parse(jwkJson); - if (jwk instanceof AsymmetricJWK) { - publicKey = ((AsymmetricJWK) jwk).toPublicKey(); - } + jwk = JWK.parse(jwkJson); } catch (Exception ignored) { } - if (publicKey == null) { + if (jwk == null) { OAuth2Error error = createOAuth2Error("jwk header is missing or invalid."); return OAuth2TokenValidatorResult.failure(error); } String jwkThumbprint; try { - jwkThumbprint = computeSHA256(publicKey); + jwkThumbprint = jwk.computeThumbprint().toString(); } catch (Exception ex) { OAuth2Error error = createOAuth2Error("Failed to compute SHA-256 Thumbprint for jwk."); diff --git a/oauth2/oauth2-resource-server/src/test/java/org/springframework/security/oauth2/server/resource/authentication/DPoPAuthenticationProviderTests.java b/oauth2/oauth2-resource-server/src/test/java/org/springframework/security/oauth2/server/resource/authentication/DPoPAuthenticationProviderTests.java index 08aec38900..bc2d83101e 100644 --- a/oauth2/oauth2-resource-server/src/test/java/org/springframework/security/oauth2/server/resource/authentication/DPoPAuthenticationProviderTests.java +++ b/oauth2/oauth2-resource-server/src/test/java/org/springframework/security/oauth2/server/resource/authentication/DPoPAuthenticationProviderTests.java @@ -26,6 +26,7 @@ import java.util.HashMap; import java.util.Map; import java.util.UUID; +import com.nimbusds.jose.jwk.JWK; import com.nimbusds.jose.jwk.JWKSet; import com.nimbusds.jose.jwk.source.JWKSource; import com.nimbusds.jose.proc.SecurityContext; @@ -218,8 +219,8 @@ public class DPoPAuthenticationProviderTests { @Test public void authenticateWhenJktDoesNotMatchThenThrowOAuth2AuthenticationException() throws Exception { - // Use different client public key - Jwt accessToken = generateAccessToken(TestKeys.DEFAULT_EC_KEY_PAIR.getPublic()); + // Use different jwk to make it not match + Jwt accessToken = generateAccessToken(TestJwks.DEFAULT_EC_JWK); JwtAuthenticationToken jwtAuthenticationToken = new JwtAuthenticationToken(accessToken); given(this.tokenAuthenticationManager.authenticate(any())).willReturn(jwtAuthenticationToken); @@ -285,14 +286,14 @@ public class DPoPAuthenticationProviderTests { } private Jwt generateAccessToken() { - return generateAccessToken(TestKeys.DEFAULT_PUBLIC_KEY); + return generateAccessToken(TestJwks.DEFAULT_RSA_JWK); } - private Jwt generateAccessToken(PublicKey clientPublicKey) { + private Jwt generateAccessToken(JWK clientJwk) { Map jktClaim = null; - if (clientPublicKey != null) { + if (clientJwk != null) { try { - String sha256Thumbprint = computeSHA256(clientPublicKey); + String sha256Thumbprint = clientJwk.computeThumbprint().toString(); jktClaim = new HashMap<>(); jktClaim.put("jkt", sha256Thumbprint); } @@ -321,11 +322,4 @@ public class DPoPAuthenticationProviderTests { byte[] digest = md.digest(value.getBytes(StandardCharsets.UTF_8)); return Base64.getUrlEncoder().withoutPadding().encodeToString(digest); } - - private static String computeSHA256(PublicKey publicKey) throws Exception { - MessageDigest md = MessageDigest.getInstance("SHA-256"); - byte[] digest = md.digest(publicKey.getEncoded()); - return Base64.getUrlEncoder().withoutPadding().encodeToString(digest); - } - }