From 45874d15631a3e41d0710f9e0fec255b379042aa Mon Sep 17 00:00:00 2001 From: Duane May Date: Fri, 24 Jul 2026 17:49:11 -0400 Subject: [PATCH 1/2] Improve signature validation --- .../identity/uaa/oauth/jwt/UaaMacSigner.java | 6 ++++-- .../ldap/PasswordComparisonAuthenticator.java | 3 ++- .../ExternalOAuthAuthenticationManager.java | 18 +++++++++++++----- 3 files changed, 19 insertions(+), 8 deletions(-) diff --git a/server/src/main/java/org/cloudfoundry/identity/uaa/oauth/jwt/UaaMacSigner.java b/server/src/main/java/org/cloudfoundry/identity/uaa/oauth/jwt/UaaMacSigner.java index e95447cd7b7..cdee2109d76 100644 --- a/server/src/main/java/org/cloudfoundry/identity/uaa/oauth/jwt/UaaMacSigner.java +++ b/server/src/main/java/org/cloudfoundry/identity/uaa/oauth/jwt/UaaMacSigner.java @@ -18,6 +18,7 @@ import javax.crypto.SecretKey; import javax.crypto.spec.SecretKeySpec; import java.nio.charset.StandardCharsets; +import java.security.MessageDigest; import java.text.ParseException; import java.util.Collections; import java.util.LinkedHashSet; @@ -84,8 +85,9 @@ public static JWTClaimsSet verify(String jwToken, JWKSet jwkSet) { String kid = header.getKeyID(); JWK jwKey = jwkSet.getKeys().stream().filter(e -> kid.equals(e.getKeyID())).findFirst().orElseThrow(); UaaMacSigner internal = new UaaMacSigner((String) jwKey.toJSONObject().get(JWKParameterNames.OCT_KEY_VALUE)); - // symmetric signature check: create internal signature and compare if matches the signature from token - if (token.getSignature().equals(internal.sign(header, token.getSigningInput())) && SUPPORTED_ALGORITHMS.contains(header.getAlgorithm())) { + // symmetric signature check: create internal signature and compare if matches the signature from token, using a constant-time comparison + Base64URL expectedSignature = internal.sign(header, token.getSigningInput()); + if (MessageDigest.isEqual(token.getSignature().decode(), expectedSignature.decode()) && SUPPORTED_ALGORITHMS.contains(header.getAlgorithm())) { return token.getJWTClaimsSet(); } throw new InvalidSignatureException("HMAC not mached"); diff --git a/server/src/main/java/org/cloudfoundry/identity/uaa/provider/ldap/PasswordComparisonAuthenticator.java b/server/src/main/java/org/cloudfoundry/identity/uaa/provider/ldap/PasswordComparisonAuthenticator.java index c16844f4403..91d1dc78509 100644 --- a/server/src/main/java/org/cloudfoundry/identity/uaa/provider/ldap/PasswordComparisonAuthenticator.java +++ b/server/src/main/java/org/cloudfoundry/identity/uaa/provider/ldap/PasswordComparisonAuthenticator.java @@ -34,6 +34,7 @@ import javax.naming.NamingException; import javax.naming.directory.Attribute; import javax.naming.directory.Attributes; +import java.security.MessageDigest; import java.util.Arrays; import java.util.function.Supplier; @@ -117,7 +118,7 @@ public DirContextOperations localCompareAuthenticate(DirContextOperations user, } else { String encodedPassword = passwordEncoder.encode(password); byte[] passwordBytes = Utf8.encode(encodedPassword); - match = Arrays.equals(passwordBytes, valBytes); + match = MessageDigest.isEqual(passwordBytes, valBytes); } } } diff --git a/server/src/main/java/org/cloudfoundry/identity/uaa/provider/oauth/ExternalOAuthAuthenticationManager.java b/server/src/main/java/org/cloudfoundry/identity/uaa/provider/oauth/ExternalOAuthAuthenticationManager.java index 9ec2c1d1025..2ffcb898f71 100644 --- a/server/src/main/java/org/cloudfoundry/identity/uaa/provider/oauth/ExternalOAuthAuthenticationManager.java +++ b/server/src/main/java/org/cloudfoundry/identity/uaa/provider/oauth/ExternalOAuthAuthenticationManager.java @@ -98,6 +98,7 @@ import java.net.URISyntaxException; import java.net.URL; import java.nio.charset.StandardCharsets; +import java.security.MessageDigest; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; @@ -658,13 +659,20 @@ protected > Map it.get("algorithm")) .orElse(null); - if (algorithm != null && !"HMAC-SHA256".equals(algorithm)) { - log.debug("Unknown algorithm was used to sign request! No claims returned."); + if (!"HMAC-SHA256".equals(algorithm)) { + log.debug("Missing or unknown algorithm was used to sign request! No claims returned."); return null; } - //check if data is signed correctly - if (!hmacSignAndEncode(signedRequests[1], secret).equals(signature)) { - log.debug("Signature is not correct, possibly the data was tampered with! No claims returned."); + // check if data is signed correctly using constant-time comparison + try { + byte[] expectedMac = Base64.decodeBase64(hmacSignAndEncode(signedRequests[1], secret)); + byte[] suppliedMac = Base64.decodeBase64(signature); + if (!MessageDigest.isEqual(expectedMac, suppliedMac)) { + log.debug("Signature is not correct, possibly the data was tampered with! No claims returned."); + return null; + } + } catch (Exception _) { + log.debug("Failed to validate signature due to encoding or computation error! No claims returned."); return null; } return jsonData; From 80665438b3818de995e0e7f0bf5d952fc45c9a8b Mon Sep 17 00:00:00 2001 From: Duane May Date: Fri, 24 Jul 2026 18:08:17 -0400 Subject: [PATCH 2/2] Incorporate Feedback --- .../identity/uaa/oauth/jwt/UaaMacSigner.java | 10 ++++++---- .../provider/ldap/PasswordComparisonAuthenticator.java | 1 - 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/server/src/main/java/org/cloudfoundry/identity/uaa/oauth/jwt/UaaMacSigner.java b/server/src/main/java/org/cloudfoundry/identity/uaa/oauth/jwt/UaaMacSigner.java index cdee2109d76..b96651a86ed 100644 --- a/server/src/main/java/org/cloudfoundry/identity/uaa/oauth/jwt/UaaMacSigner.java +++ b/server/src/main/java/org/cloudfoundry/identity/uaa/oauth/jwt/UaaMacSigner.java @@ -86,11 +86,13 @@ public static JWTClaimsSet verify(String jwToken, JWKSet jwkSet) { JWK jwKey = jwkSet.getKeys().stream().filter(e -> kid.equals(e.getKeyID())).findFirst().orElseThrow(); UaaMacSigner internal = new UaaMacSigner((String) jwKey.toJSONObject().get(JWKParameterNames.OCT_KEY_VALUE)); // symmetric signature check: create internal signature and compare if matches the signature from token, using a constant-time comparison - Base64URL expectedSignature = internal.sign(header, token.getSigningInput()); - if (MessageDigest.isEqual(token.getSignature().decode(), expectedSignature.decode()) && SUPPORTED_ALGORITHMS.contains(header.getAlgorithm())) { - return token.getJWTClaimsSet(); + if (SUPPORTED_ALGORITHMS.contains(header.getAlgorithm())) { + Base64URL expectedSignature = internal.sign(header, token.getSigningInput()); + if (MessageDigest.isEqual(token.getSignature().decode(), expectedSignature.decode())) { + return token.getJWTClaimsSet(); + } } - throw new InvalidSignatureException("HMAC not mached"); + throw new InvalidSignatureException("HMAC not matched"); } catch (ParseException | JOSEException e) { throw new InvalidSignatureException("Invalid signed token", e); } diff --git a/server/src/main/java/org/cloudfoundry/identity/uaa/provider/ldap/PasswordComparisonAuthenticator.java b/server/src/main/java/org/cloudfoundry/identity/uaa/provider/ldap/PasswordComparisonAuthenticator.java index 91d1dc78509..738358440fd 100644 --- a/server/src/main/java/org/cloudfoundry/identity/uaa/provider/ldap/PasswordComparisonAuthenticator.java +++ b/server/src/main/java/org/cloudfoundry/identity/uaa/provider/ldap/PasswordComparisonAuthenticator.java @@ -35,7 +35,6 @@ import javax.naming.directory.Attribute; import javax.naming.directory.Attributes; import java.security.MessageDigest; -import java.util.Arrays; import java.util.function.Supplier; /**