From 6ea3c8aedfb52f08658ee7dac237370e5c73e0d5 Mon Sep 17 00:00:00 2001 From: Alexander Schwartz Date: Thu, 11 Sep 2025 17:05:40 +0200 Subject: [PATCH] Session IDs and auth codes should have 128 bits of entropy Closes #42274 Signed-off-by: Alexander Schwartz --- .../java/org/keycloak/common/util/SecretGenerator.java | 10 ++++++++++ .../infinispan/util/InfinispanKeyGenerator.java | 6 +++--- .../org/keycloak/protocol/oidc/OIDCLoginProtocol.java | 4 ++-- .../keycloak/protocol/oidc/utils/OAuth2CodeParser.java | 10 +--------- 4 files changed, 16 insertions(+), 14 deletions(-) diff --git a/common/src/main/java/org/keycloak/common/util/SecretGenerator.java b/common/src/main/java/org/keycloak/common/util/SecretGenerator.java index 20d8ad82cc9..c887223dd37 100644 --- a/common/src/main/java/org/keycloak/common/util/SecretGenerator.java +++ b/common/src/main/java/org/keycloak/common/util/SecretGenerator.java @@ -2,6 +2,7 @@ package org.keycloak.common.util; import java.security.SecureRandom; import java.util.Random; +import java.util.UUID; public class SecretGenerator { @@ -113,4 +114,13 @@ public class SecretGenerator { public static int equivalentEntropySize(int length, int srcAlphabetLength, int dstAlphabetLeng) { return (int) Math.ceil(length * ((Math.log(srcAlphabetLength)) / (Math.log(dstAlphabetLeng)))); } + + /** + * Returns a pseudo-UUID where all bits are random to have a maximum entropy. + * + * @return UUID with all bits random + */ + public UUID generateSecureUUID() { + return new UUID(random.get().nextLong(), random.get().nextLong()); + } } diff --git a/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/util/InfinispanKeyGenerator.java b/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/util/InfinispanKeyGenerator.java index 8559684d2dd..f7669dc5592 100644 --- a/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/util/InfinispanKeyGenerator.java +++ b/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/util/InfinispanKeyGenerator.java @@ -27,8 +27,8 @@ import org.infinispan.affinity.KeyAffinityService; import org.infinispan.affinity.KeyAffinityServiceFactory; import org.infinispan.affinity.KeyGenerator; import org.jboss.logging.Logger; +import org.keycloak.common.util.SecretGenerator; import org.keycloak.models.KeycloakSession; -import org.keycloak.models.utils.KeycloakModelUtils; import org.keycloak.sessions.StickySessionEncoderProvider; /** @@ -88,7 +88,7 @@ public class InfinispanKeyGenerator { @Override public UUID getKey() { - return UUID.randomUUID(); + return SecretGenerator.getInstance().generateSecureUUID(); } } @@ -97,7 +97,7 @@ public class InfinispanKeyGenerator { @Override public String getKey() { - return KeycloakModelUtils.generateId(); + return SecretGenerator.getInstance().generateSecureID(); } } } diff --git a/services/src/main/java/org/keycloak/protocol/oidc/OIDCLoginProtocol.java b/services/src/main/java/org/keycloak/protocol/oidc/OIDCLoginProtocol.java index 89ee96675b4..424ad2783cd 100755 --- a/services/src/main/java/org/keycloak/protocol/oidc/OIDCLoginProtocol.java +++ b/services/src/main/java/org/keycloak/protocol/oidc/OIDCLoginProtocol.java @@ -26,6 +26,7 @@ import org.keycloak.OAuthErrorException; import org.keycloak.TokenIdGenerator; import org.keycloak.authentication.AuthenticationProcessor; import org.keycloak.authentication.RequiredActionProvider; +import org.keycloak.common.util.SecretGenerator; import org.keycloak.common.util.Time; import org.keycloak.connections.httpclient.HttpClientProvider; import org.keycloak.constants.AdapterConstants; @@ -65,7 +66,6 @@ import org.keycloak.util.TokenUtil; import java.io.IOException; import java.net.URI; import java.util.Optional; -import java.util.UUID; import static org.keycloak.protocol.oidc.grants.device.DeviceGrantType.approveOAuth2DeviceAuthorization; import static org.keycloak.protocol.oidc.grants.device.DeviceGrantType.denyOAuth2DeviceAuthorization; @@ -251,7 +251,7 @@ public class OIDCLoginProtocol implements LoginProtocol { // Standard or hybrid flow String code = null; if (responseType.hasResponseType(OIDCResponseType.CODE)) { - OAuth2Code codeData = new OAuth2Code(UUID.randomUUID().toString(), + OAuth2Code codeData = new OAuth2Code(SecretGenerator.getInstance().generateSecureID(), Time.currentTime() + userSession.getRealm().getAccessCodeLifespan(), nonce, authSession.getClientNote(OAuth2Constants.SCOPE), diff --git a/services/src/main/java/org/keycloak/protocol/oidc/utils/OAuth2CodeParser.java b/services/src/main/java/org/keycloak/protocol/oidc/utils/OAuth2CodeParser.java index baa99081de2..321d78a4621 100644 --- a/services/src/main/java/org/keycloak/protocol/oidc/utils/OAuth2CodeParser.java +++ b/services/src/main/java/org/keycloak/protocol/oidc/utils/OAuth2CodeParser.java @@ -82,21 +82,13 @@ public class OAuth2CodeParser { return result.illegalCode(); } + String codeUUID = parsed[0]; String userSessionId = parsed[1]; String clientUUID = parsed[2]; event.detail(Details.CODE_ID, userSessionId); event.session(userSessionId); - // Parse UUID - String codeUUID; - try { - codeUUID = parsed[0]; - } catch (IllegalArgumentException re) { - logger.warn("Invalid format of the UUID in the code"); - return result.illegalCode(); - } - // Retrieve UserSession var userSessionProvider = session.sessions(); UserSessionModel userSession = userSessionProvider.getUserSessionIfClientExists(realm, userSessionId, false, clientUUID);