From dee846623cc1cb4d36d936708331cd1202abdfef Mon Sep 17 00:00:00 2001 From: dxbjavid Date: Tue, 2 Jun 2026 16:53:16 +0530 Subject: [PATCH 1/2] compare pkce code verifier and client secret hash in constant time --- .../oauth2/grants/code/AuthorizationCodeGrantHandler.java | 6 +++++- .../security/oauth2/provider/ClientSecretHashVerifier.java | 7 ++++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/grants/code/AuthorizationCodeGrantHandler.java b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/grants/code/AuthorizationCodeGrantHandler.java index 39e6c79cfb5..562827b8d8f 100644 --- a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/grants/code/AuthorizationCodeGrantHandler.java +++ b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/grants/code/AuthorizationCodeGrantHandler.java @@ -19,6 +19,8 @@ package org.apache.cxf.rs.security.oauth2.grants.code; +import java.nio.charset.StandardCharsets; +import java.security.MessageDigest; import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -183,7 +185,9 @@ private boolean compareCodeVerifierWithChallenge(Client c, String clientCodeVeri codeVerifierTransformer = defaultCodeVerifierTransformer; } String transformedCodeVerifier = codeVerifierTransformer.transformCodeVerifier(clientCodeVerifier); - return clientCodeChallenge.equals(transformedCodeVerifier); + return transformedCodeVerifier != null + && MessageDigest.isEqual(clientCodeChallenge.getBytes(StandardCharsets.UTF_8), + transformedCodeVerifier.getBytes(StandardCharsets.UTF_8)); } } diff --git a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/provider/ClientSecretHashVerifier.java b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/provider/ClientSecretHashVerifier.java index f37cbed6501..81685753620 100644 --- a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/provider/ClientSecretHashVerifier.java +++ b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/provider/ClientSecretHashVerifier.java @@ -19,6 +19,9 @@ package org.apache.cxf.rs.security.oauth2.provider; +import java.nio.charset.StandardCharsets; +import java.security.MessageDigest; + import org.apache.cxf.common.util.StringUtils; import org.apache.cxf.rs.security.oauth2.common.Client; import org.apache.cxf.rt.security.crypto.MessageDigestUtils; @@ -31,7 +34,9 @@ public class ClientSecretHashVerifier implements ClientSecretVerifier { public boolean validateClientSecret(Client client, String clientSecret) { String hash = MessageDigestUtils.generate(StringUtils.toBytesUTF8(clientSecret), hashAlgorithm); - return hash.equals(client.getClientSecret()); + return client.getClientSecret() != null + && MessageDigest.isEqual(hash.getBytes(StandardCharsets.UTF_8), + client.getClientSecret().getBytes(StandardCharsets.UTF_8)); } public void setHashAlgorithm(String hashAlgorithm) { this.hashAlgorithm = hashAlgorithm; From 7abc49123b0d812ba75cf40cab808faae730d826 Mon Sep 17 00:00:00 2001 From: dxbjavid Date: Wed, 3 Jun 2026 16:44:14 +0530 Subject: [PATCH 2/2] use OAuthUtils.compareTokens for the constant-time comparisons --- .../oauth2/grants/code/AuthorizationCodeGrantHandler.java | 6 +----- .../oauth2/provider/ClientSecretHashVerifier.java | 8 ++------ 2 files changed, 3 insertions(+), 11 deletions(-) diff --git a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/grants/code/AuthorizationCodeGrantHandler.java b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/grants/code/AuthorizationCodeGrantHandler.java index 562827b8d8f..6331b732003 100644 --- a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/grants/code/AuthorizationCodeGrantHandler.java +++ b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/grants/code/AuthorizationCodeGrantHandler.java @@ -19,8 +19,6 @@ package org.apache.cxf.rs.security.oauth2.grants.code; -import java.nio.charset.StandardCharsets; -import java.security.MessageDigest; import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -185,9 +183,7 @@ private boolean compareCodeVerifierWithChallenge(Client c, String clientCodeVeri codeVerifierTransformer = defaultCodeVerifierTransformer; } String transformedCodeVerifier = codeVerifierTransformer.transformCodeVerifier(clientCodeVerifier); - return transformedCodeVerifier != null - && MessageDigest.isEqual(clientCodeChallenge.getBytes(StandardCharsets.UTF_8), - transformedCodeVerifier.getBytes(StandardCharsets.UTF_8)); + return OAuthUtils.compareTokens(clientCodeChallenge, transformedCodeVerifier); } } diff --git a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/provider/ClientSecretHashVerifier.java b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/provider/ClientSecretHashVerifier.java index 81685753620..9c8cd76aeb0 100644 --- a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/provider/ClientSecretHashVerifier.java +++ b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/provider/ClientSecretHashVerifier.java @@ -19,11 +19,9 @@ package org.apache.cxf.rs.security.oauth2.provider; -import java.nio.charset.StandardCharsets; -import java.security.MessageDigest; - import org.apache.cxf.common.util.StringUtils; import org.apache.cxf.rs.security.oauth2.common.Client; +import org.apache.cxf.rs.security.oauth2.utils.OAuthUtils; import org.apache.cxf.rt.security.crypto.MessageDigestUtils; /** @@ -34,9 +32,7 @@ public class ClientSecretHashVerifier implements ClientSecretVerifier { public boolean validateClientSecret(Client client, String clientSecret) { String hash = MessageDigestUtils.generate(StringUtils.toBytesUTF8(clientSecret), hashAlgorithm); - return client.getClientSecret() != null - && MessageDigest.isEqual(hash.getBytes(StandardCharsets.UTF_8), - client.getClientSecret().getBytes(StandardCharsets.UTF_8)); + return OAuthUtils.compareTokens(hash, client.getClientSecret()); } public void setHashAlgorithm(String hashAlgorithm) { this.hashAlgorithm = hashAlgorithm;