mirror of
https://github.com/keycloak/keycloak.git
synced 2026-01-09 23:12:06 -03:30
Access Token IDs have less than 128 bits of entropy
Closes #38663 Signed-off-by: Douglas Palmer <dpalmer@redhat.com>
This commit is contained in:
parent
6309893a63
commit
a981f6b6d5
@ -24,7 +24,6 @@ import java.security.cert.X509Certificate;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import javax.security.auth.x500.X500Principal;
|
||||
import javax.xml.crypto.dsig.keyinfo.KeyInfo;
|
||||
@ -34,6 +33,7 @@ import org.jboss.logging.Logger;
|
||||
import org.keycloak.adapters.cloned.HttpAdapterUtils;
|
||||
import org.keycloak.adapters.cloned.HttpClientAdapterException;
|
||||
import org.keycloak.common.util.MultivaluedHashMap;
|
||||
import org.keycloak.common.util.SecretGenerator;
|
||||
import org.keycloak.common.util.Time;
|
||||
import org.keycloak.dom.saml.v2.metadata.KeyTypes;
|
||||
import org.keycloak.rotation.KeyLocator;
|
||||
@ -179,7 +179,7 @@ public class SamlDescriptorPublicKeyLocator implements KeyLocator {
|
||||
this.publicKeyCacheByKey.put(new KeyHash(x509certificate.getPublicKey()), x509certificate.getPublicKey());
|
||||
} else {
|
||||
final X500Principal principal = x509certificate.getSubjectX500Principal();
|
||||
String name = (principal == null ? "unnamed" : principal.getName()) + "@" + x509certificate.getSerialNumber() + "$" + UUID.randomUUID();
|
||||
String name = (principal == null ? "unnamed" : principal.getName()) + "@" + x509certificate.getSerialNumber() + "$" + SecretGenerator.getInstance().generateSecureID();
|
||||
this.publicKeyCacheByName.put(name, x509certificate.getPublicKey());
|
||||
this.publicKeyCacheByKey.put(new KeyHash(x509certificate.getPublicKey()), x509certificate.getPublicKey());
|
||||
LOG.tracef("Adding certificate %s without a specific key name: %s", name, x509certificate);
|
||||
|
||||
@ -30,6 +30,15 @@ public class SecretGenerator {
|
||||
public static SecretGenerator getInstance() {
|
||||
return instance;
|
||||
}
|
||||
|
||||
public String generateSecureID() {
|
||||
StringBuilder builder = new StringBuilder(instance.randomBytesHex(16));
|
||||
builder.insert(8, '-');
|
||||
builder.insert(13, '-');
|
||||
builder.insert(18, '-');
|
||||
builder.insert(23, '-');
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
public String randomString() {
|
||||
return randomString(SECRET_LENGTH_256_BITS, ALPHANUM);
|
||||
@ -71,6 +80,15 @@ public class SecretGenerator {
|
||||
return buf;
|
||||
}
|
||||
|
||||
public String randomBytesHex(int length) {
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
for (byte b : randomBytes(length)) {
|
||||
sb.append(Character.forDigit((b >> 4) & 0xF, 16));
|
||||
sb.append(Character.forDigit((b & 0xF), 16));
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the equivalent length for a destination alphabet to have the same
|
||||
* entropy bits than a byte array random generated.
|
||||
|
||||
@ -19,9 +19,9 @@ package org.keycloak;
|
||||
|
||||
import org.keycloak.common.enums.RelativeUrlsUsed;
|
||||
import org.keycloak.common.util.KeycloakUriBuilder;
|
||||
import org.keycloak.common.util.SecretGenerator;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
/**
|
||||
@ -43,7 +43,7 @@ public class AbstractOAuthClient {
|
||||
protected boolean isSecure;
|
||||
protected boolean publicClient;
|
||||
protected String getStateCode() {
|
||||
return counter.getAndIncrement() + "/" + UUID.randomUUID().toString();
|
||||
return counter.getAndIncrement() + "/" + SecretGenerator.getInstance().generateSecureID();
|
||||
}
|
||||
|
||||
public String getClientId() {
|
||||
|
||||
@ -17,7 +17,8 @@
|
||||
|
||||
package org.keycloak;
|
||||
|
||||
import java.util.UUID;
|
||||
import org.keycloak.common.util.SecretGenerator;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
/**
|
||||
@ -28,6 +29,6 @@ public class TokenIdGenerator {
|
||||
private static final AtomicLong counter = new AtomicLong();
|
||||
|
||||
public static String generateId() {
|
||||
return UUID.randomUUID().toString() + "-" + System.currentTimeMillis();
|
||||
return SecretGenerator.getInstance().generateSecureID() + "-" + System.currentTimeMillis();
|
||||
}
|
||||
}
|
||||
|
||||
@ -20,11 +20,11 @@ package org.keycloak.protocol.oidc.client.authentication;
|
||||
import java.security.KeyPair;
|
||||
import java.security.PublicKey;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
import org.keycloak.OAuth2Constants;
|
||||
import org.keycloak.common.util.KeyUtils;
|
||||
import org.keycloak.common.util.KeystoreUtil;
|
||||
import org.keycloak.common.util.SecretGenerator;
|
||||
import org.keycloak.common.util.Time;
|
||||
import org.keycloak.crypto.Algorithm;
|
||||
import org.keycloak.crypto.AsymmetricSignatureSignerContext;
|
||||
@ -177,7 +177,7 @@ public class JWTClientCredentialsProvider implements ClientCredentialsProvider {
|
||||
|
||||
protected JsonWebToken createRequestToken(String clientId, String realmInfoUrl) {
|
||||
JsonWebToken reqToken = new JsonWebToken();
|
||||
reqToken.id(UUID.randomUUID().toString());
|
||||
reqToken.id(SecretGenerator.getInstance().generateSecureID());
|
||||
reqToken.issuer(clientId);
|
||||
reqToken.subject(clientId);
|
||||
reqToken.audience(realmInfoUrl);
|
||||
|
||||
@ -18,13 +18,13 @@ package org.keycloak.protocol.oidc.client.authentication;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
import javax.crypto.SecretKey;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
|
||||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.OAuth2Constants;
|
||||
import org.keycloak.common.util.SecretGenerator;
|
||||
import org.keycloak.common.util.Time;
|
||||
import org.keycloak.crypto.Algorithm;
|
||||
import org.keycloak.crypto.JavaAlgorithm;
|
||||
@ -126,7 +126,7 @@ public class JWTClientSecretCredentialsProvider implements ClientCredentialsProv
|
||||
// JWT claims is the same as one by private_key_jwt
|
||||
|
||||
JsonWebToken reqToken = new JsonWebToken();
|
||||
reqToken.id(UUID.randomUUID().toString());
|
||||
reqToken.id(SecretGenerator.getInstance().generateSecureID());
|
||||
reqToken.issuer(clientId);
|
||||
reqToken.subject(clientId);
|
||||
reqToken.audience(realmInfoUrl);
|
||||
|
||||
@ -19,6 +19,7 @@ package org.keycloak.client.cli.util;
|
||||
import org.keycloak.client.cli.config.ConfigData;
|
||||
import org.keycloak.client.cli.config.RealmConfigData;
|
||||
import org.keycloak.common.util.KeystoreUtil;
|
||||
import org.keycloak.common.util.SecretGenerator;
|
||||
import org.keycloak.common.util.Time;
|
||||
import org.keycloak.jose.jws.JWSBuilder;
|
||||
import org.keycloak.representations.AccessTokenResponse;
|
||||
@ -30,7 +31,6 @@ import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.security.KeyPair;
|
||||
import java.util.UUID;
|
||||
|
||||
import static java.lang.System.currentTimeMillis;
|
||||
import static org.keycloak.client.cli.util.ConfigUtil.checkServerInfo;
|
||||
@ -197,7 +197,7 @@ public class AuthUtil {
|
||||
KeyPair keypair = KeystoreUtil.loadKeyPairFromKeystore(keystore, storePass, keyPass, alias, keystoreType);
|
||||
|
||||
JsonWebToken reqToken = new JsonWebToken();
|
||||
reqToken.id(UUID.randomUUID().toString());
|
||||
reqToken.id(SecretGenerator.getInstance().generateSecureID());
|
||||
reqToken.issuer(clientId);
|
||||
reqToken.subject(clientId);
|
||||
reqToken.audience(realmInfoUrl);
|
||||
|
||||
@ -20,7 +20,6 @@ package org.keycloak.cluster.infinispan;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
@ -43,6 +42,7 @@ import org.keycloak.cluster.ClusterProvider;
|
||||
import org.keycloak.cluster.ExecutionResult;
|
||||
import org.keycloak.common.util.ConcurrentMultivaluedHashMap;
|
||||
import org.keycloak.common.util.Retry;
|
||||
import org.keycloak.common.util.SecretGenerator;
|
||||
import org.keycloak.common.util.Time;
|
||||
import org.keycloak.connections.infinispan.TopologyInfo;
|
||||
import org.keycloak.models.sessions.infinispan.CacheDecorators;
|
||||
@ -192,7 +192,7 @@ public class InfinispanClusterProvider implements ClusterProvider {
|
||||
}
|
||||
var wrappedEvent = WrapperClusterEvent.wrap(taskKey, events, myAddress, mySite, dcNotify, ignoreSender);
|
||||
|
||||
String eventKey = UUID.randomUUID().toString();
|
||||
String eventKey = SecretGenerator.getInstance().generateSecureID();
|
||||
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.tracef("Sending event with key %s: %s", eventKey, events);
|
||||
|
||||
@ -20,7 +20,6 @@ package org.keycloak.cluster.infinispan.remote;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
import java.util.concurrent.Executor;
|
||||
@ -43,6 +42,7 @@ import org.keycloak.cluster.infinispan.TaskCallback;
|
||||
import org.keycloak.cluster.infinispan.WrapperClusterEvent;
|
||||
import org.keycloak.common.util.ConcurrentMultivaluedHashMap;
|
||||
import org.keycloak.common.util.Retry;
|
||||
import org.keycloak.common.util.SecretGenerator;
|
||||
import org.keycloak.connections.infinispan.TopologyInfo;
|
||||
|
||||
import static org.keycloak.cluster.infinispan.InfinispanClusterProvider.TASK_KEY_PREFIX;
|
||||
@ -90,7 +90,7 @@ public class RemoteInfinispanNotificationManager {
|
||||
}
|
||||
var wrappedEvent = WrapperClusterEvent.wrap(taskKey, events, topologyInfo.getMyNodeName(), topologyInfo.getMySiteName(), dcNotify, ignoreSender);
|
||||
|
||||
var eventKey = UUID.randomUUID().toString();
|
||||
var eventKey = SecretGenerator.getInstance().generateSecureID();
|
||||
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.tracef("Sending event with key %s: %s", eventKey, events);
|
||||
|
||||
@ -19,6 +19,7 @@ package org.keycloak.broker.oidc;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.common.util.SecretGenerator;
|
||||
import org.keycloak.crypto.KeyType;
|
||||
import org.keycloak.crypto.KeyUse;
|
||||
import org.keycloak.http.HttpRequest;
|
||||
@ -55,7 +56,6 @@ import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.models.UserSessionModel;
|
||||
import org.keycloak.models.utils.KeycloakModelUtils;
|
||||
import org.keycloak.protocol.oidc.OIDCLoginProtocol;
|
||||
import org.keycloak.protocol.oidc.TokenExchangeContext;
|
||||
import org.keycloak.protocol.oidc.TokenExchangeProvider;
|
||||
@ -451,7 +451,7 @@ public abstract class AbstractOAuth2IdentityProvider<C extends OAuth2IdentityPro
|
||||
|
||||
protected JsonWebToken generateToken() {
|
||||
JsonWebToken jwt = new JsonWebToken();
|
||||
jwt.id(KeycloakModelUtils.generateId());
|
||||
jwt.id(SecretGenerator.getInstance().generateSecureID());
|
||||
jwt.type(OAuth2Constants.JWT);
|
||||
jwt.issuer(getConfig().getClientId());
|
||||
jwt.subject(getConfig().getClientId());
|
||||
|
||||
@ -19,6 +19,7 @@ package org.keycloak.jose.jws;
|
||||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.Token;
|
||||
import org.keycloak.TokenCategory;
|
||||
import org.keycloak.common.util.SecretGenerator;
|
||||
import org.keycloak.common.util.Time;
|
||||
import org.keycloak.crypto.Algorithm;
|
||||
import org.keycloak.crypto.CekManagementProvider;
|
||||
@ -42,16 +43,13 @@ import org.keycloak.models.ClientModel;
|
||||
import org.keycloak.models.ClientSessionContext;
|
||||
import org.keycloak.models.Constants;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.ProtocolMapperModel;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.TokenManager;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.models.utils.KeycloakModelUtils;
|
||||
import org.keycloak.protocol.ProtocolMapperUtils;
|
||||
import org.keycloak.protocol.oidc.OIDCAdvancedConfigWrapper;
|
||||
import org.keycloak.protocol.oidc.OIDCConfigAttributes;
|
||||
import org.keycloak.protocol.oidc.OIDCLoginProtocol;
|
||||
import org.keycloak.protocol.oidc.mappers.AbstractPairwiseSubMapper;
|
||||
import org.keycloak.protocol.oidc.mappers.LogoutTokenMapper;
|
||||
import org.keycloak.representations.LogoutToken;
|
||||
import org.keycloak.services.util.DefaultClientSessionContext;
|
||||
@ -352,7 +350,7 @@ public class DefaultTokenManager implements TokenManager {
|
||||
public LogoutToken initLogoutToken(ClientModel client, UserModel user,
|
||||
AuthenticatedClientSessionModel clientSession) {
|
||||
LogoutToken token = new LogoutToken();
|
||||
token.id(KeycloakModelUtils.generateId());
|
||||
token.id(SecretGenerator.getInstance().generateSecureID());
|
||||
token.issuedNow();
|
||||
// From the spec "OpenID Connect Back-Channel Logout 1.0 incorporating errata set 1" at https://openid.net/specs/openid-connect-backchannel-1_0.html
|
||||
// "OPs are encouraged to use short expiration times in Logout Tokens, preferably at most two minutes in the future [...]"
|
||||
|
||||
@ -2,6 +2,7 @@ package org.keycloak.protocol.docker;
|
||||
|
||||
import org.jboss.logging.Logger;
|
||||
import org.jboss.resteasy.reactive.server.jaxrs.ResponseBuilderImpl;
|
||||
import org.keycloak.common.util.SecretGenerator;
|
||||
import org.keycloak.events.EventBuilder;
|
||||
import org.keycloak.events.EventType;
|
||||
import org.keycloak.jose.jws.JWSBuilder;
|
||||
@ -12,7 +13,6 @@ import org.keycloak.models.KeyManager;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.UserSessionModel;
|
||||
import org.keycloak.models.utils.KeycloakModelUtils;
|
||||
import org.keycloak.protocol.ClientData;
|
||||
import org.keycloak.protocol.LoginProtocol;
|
||||
import org.keycloak.protocol.ProtocolMapperUtils;
|
||||
@ -95,7 +95,7 @@ public class DockerAuthV2Protocol implements LoginProtocol {
|
||||
final ClientModel client = clientSession.getClient();
|
||||
|
||||
DockerResponseToken responseToken = new DockerResponseToken()
|
||||
.id(KeycloakModelUtils.generateId())
|
||||
.id(SecretGenerator.getInstance().generateSecureID())
|
||||
.type(TokenUtil.TOKEN_TYPE_BEARER)
|
||||
.issuer(authSession.getClientNote(DockerAuthV2Protocol.ISSUER))
|
||||
.subject(userSession.getUser().getUsername())
|
||||
|
||||
@ -22,6 +22,7 @@ import java.util.HashMap;
|
||||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.broker.oidc.OIDCIdentityProviderConfig;
|
||||
import org.keycloak.common.Profile.Feature;
|
||||
import org.keycloak.common.util.SecretGenerator;
|
||||
import org.keycloak.http.HttpRequest;
|
||||
import org.keycloak.OAuth2Constants;
|
||||
import org.keycloak.OAuthErrorException;
|
||||
@ -967,7 +968,7 @@ public class TokenManager {
|
||||
AccessToken token = new AccessToken();
|
||||
|
||||
TokenContextEncoderProvider encoder = session.getProvider(TokenContextEncoderProvider.class);
|
||||
AccessTokenContext tokenCtx = encoder.getTokenContextFromClientSessionContext(clientSessionCtx, KeycloakModelUtils.generateId());
|
||||
AccessTokenContext tokenCtx = encoder.getTokenContextFromClientSessionContext(clientSessionCtx, SecretGenerator.getInstance().generateSecureID());
|
||||
token.id(encoder.encodeTokenId(tokenCtx));
|
||||
|
||||
token.type(formatTokenType(client, token));
|
||||
@ -1168,7 +1169,7 @@ public class TokenManager {
|
||||
AuthenticatedClientSessionModel clientSession = clientSessionCtx.getClientSession();
|
||||
final AccessToken.Confirmation confirmation = getConfirmation(clientSession, accessToken);
|
||||
refreshToken = new RefreshToken(accessToken, confirmation);
|
||||
refreshToken.id(KeycloakModelUtils.generateId());
|
||||
refreshToken.id(SecretGenerator.getInstance().generateSecureID());
|
||||
refreshToken.issuedNow();
|
||||
clientSession.setTimestamp(refreshToken.getIat().intValue());
|
||||
UserSessionModel userSession = clientSession.getUserSession();
|
||||
@ -1237,7 +1238,7 @@ public class TokenManager {
|
||||
throw new IllegalStateException("accessToken not set");
|
||||
}
|
||||
idToken = new IDToken();
|
||||
idToken.id(KeycloakModelUtils.generateId());
|
||||
idToken.id(SecretGenerator.getInstance().generateSecureID());
|
||||
idToken.type(TokenUtil.TOKEN_TYPE_ID);
|
||||
idToken.subject(userSession.getUser().getId());
|
||||
idToken.audience(client.getClientId());
|
||||
|
||||
@ -20,6 +20,7 @@ package org.keycloak.services.clientregistration;
|
||||
import org.keycloak.TokenCategory;
|
||||
import org.keycloak.TokenVerifier;
|
||||
import org.keycloak.common.VerificationException;
|
||||
import org.keycloak.common.util.SecretGenerator;
|
||||
import org.keycloak.crypto.SignatureSignerContext;
|
||||
import org.keycloak.crypto.SignatureProvider;
|
||||
import org.keycloak.crypto.SignatureVerifierContext;
|
||||
@ -28,7 +29,6 @@ import org.keycloak.models.ClientInitialAccessModel;
|
||||
import org.keycloak.models.ClientModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.utils.KeycloakModelUtils;
|
||||
import org.keycloak.protocol.oidc.TokenManager.TokenRevocationCheck;
|
||||
import org.keycloak.representations.AccessToken;
|
||||
import org.keycloak.representations.JsonWebToken;
|
||||
@ -70,7 +70,7 @@ public class ClientRegistrationTokenUtils {
|
||||
}
|
||||
|
||||
public static String updateRegistrationAccessToken(KeycloakSession session, RealmModel realm, ClientModel client, RegistrationAuth registrationAuth) {
|
||||
String id = KeycloakModelUtils.generateId();
|
||||
String id = SecretGenerator.getInstance().generateSecureID();
|
||||
client.setRegistrationToken(id);
|
||||
|
||||
RegistrationAccessToken regToken = new RegistrationAccessToken();
|
||||
|
||||
@ -787,7 +787,7 @@ public class AuthenticationManager {
|
||||
|
||||
public static IdentityCookieToken createIdentityToken(KeycloakSession keycloakSession, RealmModel realm, UserModel user, UserSessionModel session, String issuer) {
|
||||
IdentityCookieToken token = new IdentityCookieToken();
|
||||
token.id(KeycloakModelUtils.generateId());
|
||||
token.id(SecretGenerator.getInstance().generateSecureID());
|
||||
token.issuedNow();
|
||||
token.subject(user.getId());
|
||||
token.issuer(issuer);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user