[#11036] Identity Providers: Add support for elliptic curve signatures (ES256/ES384/ES512) using JWKS URL

This commit is contained in:
rmartinc 2022-06-06 19:06:57 +02:00 committed by Pedro Igor
parent 7d96f3ad5a
commit 711440e513
15 changed files with 307 additions and 40 deletions

View file

@ -57,9 +57,13 @@ public final class DerUtils {
}
public static PublicKey decodePublicKey(byte[] der) throws NoSuchAlgorithmException, InvalidKeySpecException, NoSuchProviderException {
return decodePublicKey(der, "RSA");
}
public static PublicKey decodePublicKey(byte[] der, String type) throws NoSuchAlgorithmException, InvalidKeySpecException, NoSuchProviderException {
X509EncodedKeySpec spec =
new X509EncodedKeySpec(der);
KeyFactory kf = KeyFactory.getInstance("RSA", BouncyIntegration.PROVIDER);
KeyFactory kf = KeyFactory.getInstance(type, BouncyIntegration.PROVIDER);
return kf.generatePublic(spec);
}

View file

@ -73,13 +73,23 @@ public final class PemUtils {
* @throws Exception
*/
public static PublicKey decodePublicKey(String pem) {
return decodePublicKey(pem, "RSA");
}
/**
* Decode a Public Key from a PEM string
* @param pem The pem encoded pblic key
* @param type The type of the key (RSA, EC,...)
* @return The public key or null
*/
public static PublicKey decodePublicKey(String pem, String type) {
if (pem == null) {
return null;
}
try {
byte[] der = pemToDer(pem);
return DerUtils.decodePublicKey(der);
return DerUtils.decodePublicKey(der, type);
} catch (Exception e) {
throw new PemException(e);
}

View file

@ -21,10 +21,28 @@ import org.keycloak.provider.Provider;
public interface SignatureProvider extends Provider {
static void checkKeyForSignature(KeyWrapper key, String algorithm, String type) throws SignatureException {
if (!type.equals(key.getType()) || !algorithm.equals(key.getAlgorithmOrDefault())) {
throw new SignatureException(String.format("Key with algorithm %s and type %s is incorrect for provider algorithm %s",
key.getAlgorithm(), key.getType(), algorithm));
}
}
static void checkKeyForVerification(KeyWrapper key, String algorithm, String type) throws VerificationException {
if (!type.equals(key.getType()) || !algorithm.equals(key.getAlgorithmOrDefault())) {
throw new VerificationException(String.format("Key with algorithm %s and type %s is incorrect for provider algorithm %s",
key.getAlgorithm(), key.getType(), algorithm));
}
}
SignatureSignerContext signer() throws SignatureException;
SignatureSignerContext signer(KeyWrapper key) throws SignatureException;
SignatureVerifierContext verifier(String kid) throws VerificationException;
SignatureVerifierContext verifier(KeyWrapper key) throws VerificationException;
boolean isAsymmetricAlgorithm();
@Override

View file

@ -30,13 +30,14 @@ import org.keycloak.broker.provider.util.SimpleHttp;
import org.keycloak.common.util.Base64Url;
import org.keycloak.common.util.SecretGenerator;
import org.keycloak.common.util.Time;
import org.keycloak.crypto.KeyWrapper;
import org.keycloak.crypto.SignatureProvider;
import org.keycloak.events.Details;
import org.keycloak.events.Errors;
import org.keycloak.events.EventBuilder;
import org.keycloak.events.EventType;
import org.keycloak.jose.jws.JWSInput;
import org.keycloak.jose.jws.JWSInputException;
import org.keycloak.jose.jws.crypto.RSAProvider;
import org.keycloak.keys.loader.PublicKeyStorageManager;
import org.keycloak.models.ClientModel;
import org.keycloak.models.FederatedIdentityModel;
@ -44,7 +45,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.representations.AccessTokenResponse;
import org.keycloak.representations.IDToken;
@ -70,7 +70,7 @@ import javax.ws.rs.core.UriBuilder;
import javax.ws.rs.core.UriInfo;
import java.io.IOException;
import java.security.PublicKey;
import java.nio.charset.StandardCharsets;
/**
* @author Pedro Igor
@ -530,9 +530,21 @@ public class OIDCIdentityProvider extends AbstractOAuth2IdentityProvider<OIDCIde
if (!getConfig().isValidateSignature()) return true;
try {
PublicKey publicKey = PublicKeyStorageManager.getIdentityProviderPublicKey(session, session.getContext().getRealm(), getConfig(), jws);
KeyWrapper key = PublicKeyStorageManager.getIdentityProviderKeyWrapper(session, session.getContext().getRealm(), getConfig(), jws);
if (key == null) {
logger.debugf("Failed to verify token, key not found for algorithm %s", jws.getHeader().getRawAlgorithm());
return false;
}
if (key.getAlgorithm() == null) {
key.setAlgorithm(jws.getHeader().getRawAlgorithm());
}
SignatureProvider signatureProvider = session.getProvider(SignatureProvider.class, jws.getHeader().getRawAlgorithm());
if (signatureProvider == null) {
logger.debugf("Failed to verify token, signature provider not found for algorithm %s", jws.getHeader().getRawAlgorithm());
return false;
}
return publicKey != null && RSAProvider.verify(jws, publicKey);
return signatureProvider.verifier(key).verify(jws.getEncodedSignatureInput().getBytes(StandardCharsets.UTF_8), jws.getSignature());
} catch (Exception e) {
logger.debug("Failed to verify token", e);
return false;

View file

@ -34,11 +34,23 @@ public class AsymmetricSignatureProvider implements SignatureProvider {
return new ServerAsymmetricSignatureSignerContext(session, algorithm);
}
@Override
public SignatureSignerContext signer(KeyWrapper key) throws SignatureException {
SignatureProvider.checkKeyForSignature(key, algorithm, KeyType.RSA);
return new ServerAsymmetricSignatureSignerContext(key);
}
@Override
public SignatureVerifierContext verifier(String kid) throws VerificationException {
return new ServerAsymmetricSignatureVerifierContext(session, kid, algorithm);
}
@Override
public SignatureVerifierContext verifier(KeyWrapper key) throws VerificationException {
SignatureProvider.checkKeyForVerification(key, algorithm, KeyType.RSA);
return new ServerAsymmetricSignatureVerifierContext(key);
}
@Override
public boolean isAsymmetricAlgorithm() {
return true;

View file

@ -28,11 +28,23 @@ public class ECDSASignatureProvider implements SignatureProvider {
return new ServerECDSASignatureSignerContext(session, algorithm);
}
@Override
public SignatureSignerContext signer(KeyWrapper key) throws SignatureException {
SignatureProvider.checkKeyForSignature(key, algorithm, KeyType.EC);
return new ServerECDSASignatureSignerContext(key);
}
@Override
public SignatureVerifierContext verifier(String kid) throws VerificationException {
return new ServerECDSASignatureVerifierContext(session, kid, algorithm);
}
@Override
public SignatureVerifierContext verifier(KeyWrapper key) throws VerificationException {
SignatureProvider.checkKeyForVerification(key, algorithm, KeyType.EC);
return new ServerECDSASignatureVerifierContext(key);
}
@Override
public boolean isAsymmetricAlgorithm() {
return true;

View file

@ -34,11 +34,23 @@ public class MacSecretSignatureProvider implements SignatureProvider {
return new ServerMacSignatureSignerContext(session, algorithm);
}
@Override
public SignatureSignerContext signer(KeyWrapper key) throws SignatureException {
SignatureProvider.checkKeyForSignature(key, algorithm, KeyType.OCT);
return new ServerMacSignatureSignerContext(key);
}
@Override
public SignatureVerifierContext verifier(String kid) throws VerificationException {
return new ServerMacSignatureVerifierContext(session, kid, algorithm);
}
@Override
public SignatureVerifierContext verifier(KeyWrapper key) throws VerificationException {
SignatureProvider.checkKeyForVerification(key, algorithm, KeyType.OCT);
return new ServerMacSignatureVerifierContext(key);
}
@Override
public boolean isAsymmetricAlgorithm() {
return false;

View file

@ -24,6 +24,10 @@ public class ServerAsymmetricSignatureSignerContext extends AsymmetricSignatureS
super(getKey(session, algorithm));
}
public ServerAsymmetricSignatureSignerContext(KeyWrapper key) throws SignatureException {
super(key);
}
static KeyWrapper getKey(KeycloakSession session, String algorithm) {
KeyWrapper key = session.keys().getActiveKey(session.getContext().getRealm(), KeyUse.SIG, algorithm);
if (key == null) {

View file

@ -25,6 +25,10 @@ public class ServerAsymmetricSignatureVerifierContext extends AsymmetricSignatur
super(getKey(session, kid, algorithm));
}
public ServerAsymmetricSignatureVerifierContext(KeyWrapper key) throws VerificationException {
super(key);
}
static KeyWrapper getKey(KeycloakSession session, String kid, String algorithm) throws VerificationException {
KeyWrapper key = session.keys().getKey(session.getContext().getRealm(), kid, KeyUse.SIG, algorithm);
if (key == null) {

View file

@ -24,6 +24,10 @@ public class ServerMacSignatureSignerContext extends MacSignatureSignerContext {
super(getKey(session, algorithm));
}
public ServerMacSignatureSignerContext(KeyWrapper key) throws SignatureException {
super(key);
}
private static KeyWrapper getKey(KeycloakSession session, String algorithm) {
KeyWrapper key = session.keys().getActiveKey(session.getContext().getRealm(), KeyUse.SIG, algorithm);
if (key == null) {

View file

@ -25,6 +25,10 @@ public class ServerMacSignatureVerifierContext extends MacSignatureVerifierConte
super(getKey(session, kid, algorithm));
}
public ServerMacSignatureVerifierContext(KeyWrapper key) throws VerificationException {
super(key);
}
private static KeyWrapper getKey(KeycloakSession session, String kid, String algorithm) throws VerificationException {
KeyWrapper key = session.keys().getKey(session.getContext().getRealm(), kid, KeyUse.SIG, algorithm);
if (key == null) {

View file

@ -16,8 +16,11 @@
*/
package org.keycloak.keys.loader;
import org.keycloak.common.util.Base64Url;
import org.keycloak.common.util.KeyUtils;
import org.keycloak.common.util.PemUtils;
import org.keycloak.crypto.Algorithm;
import org.keycloak.crypto.JavaAlgorithm;
import org.keycloak.crypto.KeyType;
import org.keycloak.crypto.KeyUse;
import org.keycloak.crypto.KeyWrapper;
@ -32,29 +35,41 @@ import java.util.Map;
*/
public class HardcodedPublicKeyLoader implements PublicKeyLoader {
private final String kid;
private final String pem;
private final KeyWrapper keyWrapper;
public HardcodedPublicKeyLoader(String kid, String pem) {
this.kid = kid;
this.pem = pem;
this(kid, pem, Algorithm.RS256);
}
public HardcodedPublicKeyLoader(String kid, String encodedKey, String algorithm) {
if (encodedKey != null && !encodedKey.trim().isEmpty()) {
keyWrapper = new KeyWrapper();
keyWrapper.setKid(kid);
keyWrapper.setUse(KeyUse.SIG);
// depending the algorithm load the correct key from the encoded string
if (JavaAlgorithm.isRSAJavaAlgorithm(algorithm)) {
keyWrapper.setType(KeyType.RSA);
keyWrapper.setPublicKey(PemUtils.decodePublicKey(encodedKey, KeyType.RSA));
} else if (JavaAlgorithm.isECJavaAlgorithm(algorithm)) {
keyWrapper.setType(KeyType.EC);
keyWrapper.setPublicKey(PemUtils.decodePublicKey(encodedKey, KeyType.EC));
} else if (JavaAlgorithm.isHMACJavaAlgorithm(algorithm)) {
keyWrapper.setType(KeyType.OCT);
keyWrapper.setSecretKey(KeyUtils.loadSecretKey(Base64Url.decode(encodedKey), algorithm));
}
} else {
keyWrapper = null;
}
}
@Override
public Map<String, KeyWrapper> loadKeys() throws Exception {
return Collections.unmodifiableMap(Collections.singletonMap(kid, getSavedPublicKey()));
return keyWrapper != null
? Collections.unmodifiableMap(Collections.singletonMap(keyWrapper.getKid(), getSavedPublicKey()))
: Collections.emptyMap();
}
protected KeyWrapper getSavedPublicKey() {
KeyWrapper keyWrapper = null;
if (pem != null && ! pem.trim().equals("")) {
keyWrapper = new KeyWrapper();
keyWrapper.setKid(kid);
keyWrapper.setType(KeyType.RSA);
keyWrapper.setAlgorithm(Algorithm.RS256);
keyWrapper.setUse(KeyUse.SIG);
keyWrapper.setPublicKey(PemUtils.decodePublicKey(pem));
}
return keyWrapper;
}
}

View file

@ -62,11 +62,12 @@ public class PublicKeyStorageManager {
return keyStorage.getFirstPublicKey(modelKey, algAlgorithm, loader);
}
public static PublicKey getIdentityProviderPublicKey(KeycloakSession session, RealmModel realm, OIDCIdentityProviderConfig idpConfig, JWSInput input) {
public static KeyWrapper getIdentityProviderKeyWrapper(KeycloakSession session, RealmModel realm, OIDCIdentityProviderConfig idpConfig, JWSInput input) {
boolean keyIdSetInConfiguration = idpConfig.getPublicKeySignatureVerifierKeyId() != null
&& ! idpConfig.getPublicKeySignatureVerifierKeyId().trim().isEmpty();
String kid = input.getHeader().getKeyId();
String alg = input.getHeader().getRawAlgorithm();
PublicKeyStorageProvider keyStorage = session.getProvider(PublicKeyStorageProvider.class);
@ -85,9 +86,14 @@ public class PublicKeyStorageManager {
loader = new HardcodedPublicKeyLoader(
keyIdSetInConfiguration
? idpConfig.getPublicKeySignatureVerifierKeyId().trim()
: kid, pem);
: kid, pem, alg);
}
return (PublicKey)keyStorage.getPublicKey(modelKey, kid, loader).getPublicKey();
return keyStorage.getPublicKey(modelKey, kid, loader);
}
public static PublicKey getIdentityProviderPublicKey(KeycloakSession session, RealmModel realm, OIDCIdentityProviderConfig idpConfig, JWSInput input) {
KeyWrapper key = getIdentityProviderKeyWrapper(session, realm, idpConfig, input);
return key != null? (PublicKey) key.getPublicKey() : null;
}
}

View file

@ -23,7 +23,6 @@ import org.keycloak.admin.client.resource.ClientScopeResource;
import org.keycloak.admin.client.resource.RealmResource;
import org.keycloak.admin.client.resource.RoleResource;
import org.keycloak.admin.client.resource.UserResource;
import org.keycloak.crypto.Algorithm;
import org.keycloak.crypto.KeyStatus;
import org.keycloak.crypto.KeyUse;
import org.keycloak.representations.idm.ClientRepresentation;
@ -280,4 +279,13 @@ public class ApiUtil {
return null;
}
public static KeysMetadataRepresentation.KeyMetadataRepresentation findActiveSigningKey(RealmResource realm, String alg) {
KeysMetadataRepresentation keyMetadata = realm.keys().getKeyMetadata();
for (KeysMetadataRepresentation.KeyMetadataRepresentation rep : keyMetadata.getKeys()) {
if (rep.getPublicKey() != null && KeyStatus.valueOf(rep.getStatus()).isActive() && KeyUse.SIG.equals(rep.getUse()) && alg.equals(rep.getAlgorithm())) {
return rep;
}
}
return null;
}
}

View file

@ -17,6 +17,8 @@
package org.keycloak.testsuite.broker;
import java.io.Closeable;
import java.nio.charset.StandardCharsets;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriBuilder;
@ -29,6 +31,8 @@ import org.keycloak.connections.infinispan.InfinispanConnectionProvider;
import org.keycloak.crypto.Algorithm;
import org.keycloak.keys.KeyProvider;
import org.keycloak.keys.PublicKeyStorageUtils;
import org.keycloak.models.utils.KeycloakModelUtils;
import org.keycloak.protocol.oidc.OIDCConfigAttributes;
import org.keycloak.protocol.oidc.OIDCLoginProtocolService;
import org.keycloak.representations.idm.ComponentRepresentation;
import org.keycloak.representations.idm.IdentityProviderRepresentation;
@ -37,6 +41,7 @@ import org.keycloak.representations.idm.UserRepresentation;
import org.keycloak.testsuite.Assert;
import org.keycloak.testsuite.admin.ApiUtil;
import org.keycloak.testsuite.client.resources.TestingCacheResource;
import org.keycloak.testsuite.updaters.ClientAttributeUpdater;
import org.keycloak.testsuite.util.OAuthClient;
import static org.junit.Assert.assertEquals;
@ -100,7 +105,7 @@ public class KcOIDCBrokerWithSignatureTest extends AbstractBaseBrokerTest {
logoutFromRealm(getConsumerRoot(), bc.consumerRealmName());
// Rotate public keys on the parent broker
rotateKeys();
rotateKeys(Algorithm.RS256, "rsa-generated");
// User not able to login now as new keys can't be yet downloaded (10s timeout)
logInAsUserInIDP();
@ -137,7 +142,7 @@ public class KcOIDCBrokerWithSignatureTest extends AbstractBaseBrokerTest {
cfg.setValidateSignature(true);
cfg.setUseJwksUrl(false);
KeysMetadataRepresentation.KeyMetadataRepresentation key = ApiUtil.findActiveSigningKey(providerRealm());
KeysMetadataRepresentation.KeyMetadataRepresentation key = ApiUtil.findActiveSigningKey(providerRealm(), Algorithm.RS256);
cfg.setPublicKeySignatureVerifier(key.getPublicKey());
updateIdentityProvider(idpRep);
@ -148,7 +153,7 @@ public class KcOIDCBrokerWithSignatureTest extends AbstractBaseBrokerTest {
logoutFromRealm(getConsumerRoot(), bc.consumerRealmName());
// Rotate public keys on the parent broker
rotateKeys();
rotateKeys(Algorithm.RS256, "rsa-generated");
// User not able to login now as new keys can't be yet downloaded (10s timeout)
logInAsUserInIDP();
@ -163,6 +168,89 @@ public class KcOIDCBrokerWithSignatureTest extends AbstractBaseBrokerTest {
assertErrorPage("Unexpected error when authenticating with identity provider");
}
@Test
public void testSignatureVerificationHardcodedPublicKeyES256() throws Exception {
IdentityProviderRepresentation idpRep = getIdentityProvider();
OIDCIdentityProviderConfigRep cfg = new OIDCIdentityProviderConfigRep(idpRep);
cfg.setValidateSignature(true);
cfg.setUseJwksUrl(false);
rotateKeys(Algorithm.ES256, "ecdsa-generated");
KeysMetadataRepresentation.KeyMetadataRepresentation key = ApiUtil.findActiveSigningKey(providerRealm(), Algorithm.ES256);
cfg.setPublicKeySignatureVerifier(key.getPublicKey());
updateIdentityProvider(idpRep);
try (Closeable clientUpdater = ClientAttributeUpdater.forClient(adminClient, bc.providerRealmName(), bc.getIDPClientIdInProviderRealm())
.setAttribute(OIDCConfigAttributes.ACCESS_TOKEN_SIGNED_RESPONSE_ALG, Algorithm.ES256)
.setAttribute(OIDCConfigAttributes.AUTHORIZATION_SIGNED_RESPONSE_ALG, Algorithm.ES256)
.setAttribute(OIDCConfigAttributes.ID_TOKEN_SIGNED_RESPONSE_ALG, Algorithm.ES256)
.update()) {
logInAsUserInIDPForFirstTime();
assertLoggedInAccountManagement();
logoutFromRealm(getConsumerRoot(), bc.consumerRealmName());
logInAsUserInIDP();
logoutFromRealm(getConsumerRoot(), bc.consumerRealmName());
}
}
@Test
public void testSignatureVerificationHardcodedPublicKeyPS512() throws Exception {
IdentityProviderRepresentation idpRep = getIdentityProvider();
OIDCIdentityProviderConfigRep cfg = new OIDCIdentityProviderConfigRep(idpRep);
cfg.setValidateSignature(true);
cfg.setUseJwksUrl(false);
rotateKeys(Algorithm.PS512, "rsa-generated");
KeysMetadataRepresentation.KeyMetadataRepresentation key = ApiUtil.findActiveSigningKey(providerRealm(), Algorithm.PS512);
cfg.setPublicKeySignatureVerifier(key.getPublicKey());
updateIdentityProvider(idpRep);
try (Closeable clientUpdater = ClientAttributeUpdater.forClient(adminClient, bc.providerRealmName(), bc.getIDPClientIdInProviderRealm())
.setAttribute(OIDCConfigAttributes.ACCESS_TOKEN_SIGNED_RESPONSE_ALG, Algorithm.PS512)
.setAttribute(OIDCConfigAttributes.AUTHORIZATION_SIGNED_RESPONSE_ALG, Algorithm.PS512)
.setAttribute(OIDCConfigAttributes.ID_TOKEN_SIGNED_RESPONSE_ALG, Algorithm.PS512)
.update()) {
logInAsUserInIDPForFirstTime();
assertLoggedInAccountManagement();
logoutFromRealm(getConsumerRoot(), bc.consumerRealmName());
logInAsUserInIDP();
logoutFromRealm(getConsumerRoot(), bc.consumerRealmName());
}
}
@Test
public void testSignatureVerificationHardcodedPublicKeyHS512() throws Exception {
IdentityProviderRepresentation idpRep = getIdentityProvider();
OIDCIdentityProviderConfigRep cfg = new OIDCIdentityProviderConfigRep(idpRep);
cfg.setValidateSignature(true);
cfg.setUseJwksUrl(false);
String base64secret = Base64Url.encode("01234567890123456789012345678912".getBytes(StandardCharsets.UTF_8));
createHSKey(Algorithm.HS512, "32", base64secret);
cfg.setPublicKeySignatureVerifier(base64secret);
updateIdentityProvider(idpRep);
try (Closeable clientUpdater = ClientAttributeUpdater.forClient(adminClient, bc.providerRealmName(), bc.getIDPClientIdInProviderRealm())
.setAttribute(OIDCConfigAttributes.ACCESS_TOKEN_SIGNED_RESPONSE_ALG, Algorithm.HS512)
.setAttribute(OIDCConfigAttributes.AUTHORIZATION_SIGNED_RESPONSE_ALG, Algorithm.HS512)
.setAttribute(OIDCConfigAttributes.ID_TOKEN_SIGNED_RESPONSE_ALG, Algorithm.HS512)
.update()) {
logInAsUserInIDPForFirstTime();
assertLoggedInAccountManagement();
logoutFromRealm(getConsumerRoot(), bc.consumerRealmName());
logInAsUserInIDP();
logoutFromRealm(getConsumerRoot(), bc.consumerRealmName());
}
}
@Test
public void testSignatureVerificationHardcodedPublicKeyWithKeyIdSetExplicitly() throws Exception {
// Configure OIDC identity provider with JWKS URL
@ -171,7 +259,7 @@ public class KcOIDCBrokerWithSignatureTest extends AbstractBaseBrokerTest {
cfg.setValidateSignature(true);
cfg.setUseJwksUrl(false);
KeysMetadataRepresentation.KeyMetadataRepresentation key = ApiUtil.findActiveSigningKey(providerRealm());
KeysMetadataRepresentation.KeyMetadataRepresentation key = ApiUtil.findActiveSigningKey(providerRealm(), Algorithm.RS256);
String pemData = key.getPublicKey();
cfg.setPublicKeySignatureVerifier(pemData);
String expectedKeyId = KeyUtils.createKeyId(PemUtils.decodePublicKey(pemData));
@ -269,28 +357,86 @@ public class KcOIDCBrokerWithSignatureTest extends AbstractBaseBrokerTest {
assertErrorPage("Unexpected error when authenticating with identity provider");
}
@Test
public void testSignatureVerificationJwksES256() throws Exception {
updateIdentityProviderWithJwksUrl();
rotateKeys(Algorithm.ES256, "ecdsa-generated");
try (Closeable clientUpdater = ClientAttributeUpdater.forClient(adminClient, bc.providerRealmName(), bc.getIDPClientIdInProviderRealm())
.setAttribute(OIDCConfigAttributes.ACCESS_TOKEN_SIGNED_RESPONSE_ALG, Algorithm.ES256)
.setAttribute(OIDCConfigAttributes.AUTHORIZATION_SIGNED_RESPONSE_ALG, Algorithm.ES256)
.setAttribute(OIDCConfigAttributes.ID_TOKEN_SIGNED_RESPONSE_ALG, Algorithm.ES256)
.update()) {
// Check that user is able to login with ES256
logInAsUserInIDPForFirstTime();
assertLoggedInAccountManagement();
logoutFromRealm(getConsumerRoot(), bc.consumerRealmName());
private void rotateKeys() {
String activeKid = providerRealm().keys().getKeyMetadata().getActive().get(Algorithm.RS256);
logInAsUserInIDP();
logoutFromRealm(getConsumerRoot(), bc.consumerRealmName());
}
}
@Test
public void testSignatureVerificationJwksPS512() throws Exception {
updateIdentityProviderWithJwksUrl();
try (Closeable clientUpdater = ClientAttributeUpdater.forClient(adminClient, bc.providerRealmName(), bc.getIDPClientIdInProviderRealm())
.setAttribute(OIDCConfigAttributes.ACCESS_TOKEN_SIGNED_RESPONSE_ALG, Algorithm.PS512)
.setAttribute(OIDCConfigAttributes.AUTHORIZATION_SIGNED_RESPONSE_ALG, Algorithm.PS512)
.setAttribute(OIDCConfigAttributes.ID_TOKEN_SIGNED_RESPONSE_ALG, Algorithm.PS512)
.update()) {
// Check that user is able to login with PS512
logInAsUserInIDPForFirstTime();
assertLoggedInAccountManagement();
logoutFromRealm(getConsumerRoot(), bc.consumerRealmName());
logInAsUserInIDP();
logoutFromRealm(getConsumerRoot(), bc.consumerRealmName());
}
}
private void rotateKeys(String algorithm, String providerId) {
String activeKid = providerRealm().keys().getKeyMetadata().getActive().get(algorithm);
// Rotate public keys on the parent broker
String realmId = providerRealm().toRepresentation().getId();
ComponentRepresentation keys = new ComponentRepresentation();
keys.setName("generated");
keys.setProviderType(KeyProvider.class.getName());
keys.setProviderId("rsa-generated");
keys.setProviderId(providerId);
keys.setParentId(realmId);
keys.setConfig(new MultivaluedHashMap<>());
keys.getConfig().putSingle("priority", Long.toString(System.currentTimeMillis()));
Response response = providerRealm().components().add(keys);
keys.getConfig().putSingle("algorithm", algorithm);
try (Response response = providerRealm().components().add(keys)) {
assertEquals(201, response.getStatus());
response.close();
}
String updatedActiveKid = providerRealm().keys().getKeyMetadata().getActive().get(Algorithm.RS256);
String updatedActiveKid = providerRealm().keys().getKeyMetadata().getActive().get(algorithm);
assertNotEquals(activeKid, updatedActiveKid);
}
private void createHSKey(String algorithm, String size, String secret) {
String realmId = providerRealm().toRepresentation().getId();
ComponentRepresentation keys = new ComponentRepresentation();
keys.setName("generated");
keys.setProviderType(KeyProvider.class.getName());
keys.setProviderId("hmac-generated");
keys.setParentId(realmId);
keys.setConfig(new MultivaluedHashMap<>());
keys.getConfig().putSingle("priority", Long.toString(System.currentTimeMillis()));
keys.getConfig().putSingle("algorithm", algorithm);
keys.getConfig().putSingle("secretSize", size);
keys.getConfig().putSingle("kid", KeycloakModelUtils.generateId());
keys.getConfig().putSingle("secret", secret);
try (Response response = providerRealm().components().add(keys)) {
assertEquals(201, response.getStatus());
}
String updatedActiveKid = providerRealm().keys().getKeyMetadata().getActive().get(algorithm);
Assert.assertNotNull(updatedActiveKid);
}
private RealmResource providerRealm() {
return adminClient.realm(bc.providerRealmName());
@ -307,8 +453,4 @@ public class KcOIDCBrokerWithSignatureTest extends AbstractBaseBrokerTest {
private RealmResource consumerRealm() {
return adminClient.realm(bc.consumerRealmName());
}
}