From 3c5027de3c8a5cfea1de863cd628cd86332eea60 Mon Sep 17 00:00:00 2001 From: stianst Date: Fri, 22 Jun 2018 16:05:42 +0200 Subject: [PATCH] KEYCLOAK-7701 Refactor key providers to support additional algorithms --- .../java/org/keycloak/crypto/Algorithm.java | 27 +- .../org/keycloak/crypto/JavaAlgorithm.java | 42 +++ .../java/org/keycloak/crypto/KeyStatus.java | 27 +- .../java/org/keycloak/crypto/KeyType.java | 20 +- .../main/java/org/keycloak/crypto/KeyUse.java | 19 +- .../java/org/keycloak/crypto/KeyWrapper.java | 135 ++++++++ .../idm/KeysMetadataRepresentation.java | 10 + .../keycloak/keys/AesKeyProviderFactory.java | 34 -- .../java/org/keycloak/keys/KeyProvider.java | 22 +- .../org/keycloak/keys/KeyProviderFactory.java | 14 + .../org/keycloak/keys/RsaKeyProvider.java | 60 ---- .../org/keycloak/keys/SecretKeyProvider.java | 50 --- .../java/org/keycloak/keys/KeyMetadata.java | 12 +- .../java/org/keycloak/models/KeyManager.java | 26 +- .../broker/saml/SAMLIdentityProvider.java | 33 +- .../keycloak/keys/AbstractRsaKeyProvider.java | 114 ++----- .../keys/AbstractRsaKeyProviderFactory.java | 2 +- .../org/keycloak/keys/DefaultKeyManager.java | 292 ++++++++---------- .../keycloak/keys/FailsafeAesKeyProvider.java | 20 +- .../keys/FailsafeHmacKeyProvider.java | 22 +- .../keycloak/keys/FailsafeRsaKeyProvider.java | 55 ++-- .../keys/FailsafeSecretKeyProvider.java | 62 ++-- .../keys/GeneratedAesKeyProvider.java | 7 +- .../keys/GeneratedAesKeyProviderFactory.java | 5 +- .../keys/GeneratedHmacKeyProvider.java | 7 +- .../keys/GeneratedHmacKeyProviderFactory.java | 4 +- .../keys/GeneratedRsaKeyProviderFactory.java | 12 - .../keys/GeneratedSecretKeyProvider.java | 81 ++--- .../GeneratedSecretKeyProviderFactory.java | 12 - .../keycloak/keys/ImportedRsaKeyProvider.java | 7 +- .../keys/ImportedRsaKeyProviderFactory.java | 14 - .../keys/JavaKeystoreKeyProvider.java | 19 +- .../keys/JavaKeystoreKeyProviderFactory.java | 14 +- .../oidc/OIDCLoginProtocolService.java | 3 +- .../keycloak/protocol/saml/SamlService.java | 6 +- .../SamlIDPDescriptorClientInstallation.java | 10 +- .../services/resources/admin/KeyResource.java | 60 ++-- .../org/keycloak/testsuite/admin/ApiUtil.java | 3 +- .../org/keycloak/testsuite/util/KeyUtils.java | 12 + .../keycloak/testsuite/util/OAuthClient.java | 3 +- .../OIDCPublicKeyRotationAdapterTest.java | 4 +- .../broker/KcOIDCBrokerWithSignatureTest.java | 5 +- .../broker/KcSamlSignedBrokerTest.java | 11 +- .../keys/GeneratedHmacKeyProviderTest.java | 10 +- .../keys/ImportedRsaKeyProviderTest.java | 3 +- .../testsuite/keys/KeyRotationTest.java | 7 +- .../java/org/keycloak/testsuite/ApiUtil.java | 4 - .../messages/admin-messages_en.properties | 5 +- .../theme/base/admin/resources/js/app.js | 19 +- .../partials/realm-keys-disabled.html | 71 +++++ ...keys-list.html => realm-keys-passive.html} | 23 +- .../partials/realm-keys-providers.html | 30 +- .../admin/resources/partials/realm-keys.html | 40 ++- 53 files changed, 813 insertions(+), 796 deletions(-) rename server-spi-private/src/main/java/org/keycloak/keys/HmacKeyProvider.java => core/src/main/java/org/keycloak/crypto/Algorithm.java (64%) mode change 100644 => 100755 create mode 100644 core/src/main/java/org/keycloak/crypto/JavaAlgorithm.java rename server-spi-private/src/main/java/org/keycloak/keys/AesKeyProvider.java => core/src/main/java/org/keycloak/crypto/KeyStatus.java (54%) rename server-spi-private/src/main/java/org/keycloak/keys/RsaKeyProviderFactory.java => core/src/main/java/org/keycloak/crypto/KeyType.java (61%) rename server-spi-private/src/main/java/org/keycloak/keys/HmacKeyProviderFactory.java => core/src/main/java/org/keycloak/crypto/KeyUse.java (60%) create mode 100644 core/src/main/java/org/keycloak/crypto/KeyWrapper.java delete mode 100644 server-spi-private/src/main/java/org/keycloak/keys/AesKeyProviderFactory.java delete mode 100644 server-spi-private/src/main/java/org/keycloak/keys/RsaKeyProvider.java delete mode 100644 server-spi-private/src/main/java/org/keycloak/keys/SecretKeyProvider.java create mode 100755 themes/src/main/resources/theme/base/admin/resources/partials/realm-keys-disabled.html rename themes/src/main/resources/theme/base/admin/resources/partials/{realm-keys-list.html => realm-keys-passive.html} (68%) diff --git a/server-spi-private/src/main/java/org/keycloak/keys/HmacKeyProvider.java b/core/src/main/java/org/keycloak/crypto/Algorithm.java old mode 100644 new mode 100755 similarity index 64% rename from server-spi-private/src/main/java/org/keycloak/keys/HmacKeyProvider.java rename to core/src/main/java/org/keycloak/crypto/Algorithm.java index a525598fc6bc..57c36ee0359a --- a/server-spi-private/src/main/java/org/keycloak/keys/HmacKeyProvider.java +++ b/core/src/main/java/org/keycloak/crypto/Algorithm.java @@ -14,22 +14,19 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +package org.keycloak.crypto; -package org.keycloak.keys; +public interface Algorithm { -import org.keycloak.jose.jws.AlgorithmType; - -/** - * @author Stian Thorgersen - */ -public interface HmacKeyProvider extends SecretKeyProvider { - - default AlgorithmType getType() { - return AlgorithmType.HMAC; - } - - default String getJavaAlgorithmName() { - return "HmacSHA256"; - } + String HS256 = "HS256"; + String HS384 = "HS384"; + String HS512 = "HS512"; + String RS256 = "RS256"; + String RS384 = "RS384"; + String RS512 = "RS512"; + String ES256 = "ES256"; + String ES384 = "ES384"; + String ES512 = "ES512"; + String AES = "AES"; } diff --git a/core/src/main/java/org/keycloak/crypto/JavaAlgorithm.java b/core/src/main/java/org/keycloak/crypto/JavaAlgorithm.java new file mode 100644 index 000000000000..46d084c78bbd --- /dev/null +++ b/core/src/main/java/org/keycloak/crypto/JavaAlgorithm.java @@ -0,0 +1,42 @@ +/* + * Copyright 2016 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.keycloak.crypto; + +public class JavaAlgorithm { + + public static String getJavaAlgorithm(String algorithm) { + switch (algorithm) { + case Algorithm.RS256: + return "SHA256withRSA"; + case Algorithm.RS384: + return "SHA384withRSA"; + case Algorithm.RS512: + return "SHA512withRSA"; + case Algorithm.HS256: + return "HMACSHA256"; + case Algorithm.HS384: + return "HMACSHA384"; + case Algorithm.HS512: + return "HMACSHA512"; + case Algorithm.AES: + return "AES"; + default: + throw new IllegalArgumentException("Unkown algorithm " + algorithm); + } + } + +} diff --git a/server-spi-private/src/main/java/org/keycloak/keys/AesKeyProvider.java b/core/src/main/java/org/keycloak/crypto/KeyStatus.java similarity index 54% rename from server-spi-private/src/main/java/org/keycloak/keys/AesKeyProvider.java rename to core/src/main/java/org/keycloak/crypto/KeyStatus.java index 4f9fcc2b5455..43ab125be4fb 100644 --- a/server-spi-private/src/main/java/org/keycloak/keys/AesKeyProvider.java +++ b/core/src/main/java/org/keycloak/crypto/KeyStatus.java @@ -1,5 +1,5 @@ /* - * Copyright 2017 Red Hat, Inc. and/or its affiliates + * Copyright 2016 Red Hat, Inc. and/or its affiliates * and other contributors as indicated by the @author tags. * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -14,21 +14,26 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +package org.keycloak.crypto; -package org.keycloak.keys; +public enum KeyStatus { -import org.keycloak.jose.jws.AlgorithmType; + ACTIVE, PASSIVE, DISABLED; -/** - * @author Marek Posolda - */ -public interface AesKeyProvider extends SecretKeyProvider { + public static KeyStatus from(boolean active, boolean enabled) { + if (!enabled) { + return KeyStatus.DISABLED; + } else { + return active ? KeyStatus.ACTIVE : KeyStatus.PASSIVE; + } + } - default AlgorithmType getType() { - return AlgorithmType.AES; + public boolean isActive() { + return this.equals(ACTIVE); } - default String getJavaAlgorithmName() { - return "AES"; + public boolean isEnabled() { + return this.equals(ACTIVE) || this.equals(PASSIVE); } + } diff --git a/server-spi-private/src/main/java/org/keycloak/keys/RsaKeyProviderFactory.java b/core/src/main/java/org/keycloak/crypto/KeyType.java similarity index 61% rename from server-spi-private/src/main/java/org/keycloak/keys/RsaKeyProviderFactory.java rename to core/src/main/java/org/keycloak/crypto/KeyType.java index 440103c1057a..2fcc999065c7 100644 --- a/server-spi-private/src/main/java/org/keycloak/keys/RsaKeyProviderFactory.java +++ b/core/src/main/java/org/keycloak/crypto/KeyType.java @@ -14,22 +14,12 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +package org.keycloak.crypto; -package org.keycloak.keys; +public interface KeyType { -import org.keycloak.jose.jws.AlgorithmType; - -import java.util.Collections; -import java.util.Map; - -/** - * @author Stian Thorgersen - */ -public interface RsaKeyProviderFactory extends KeyProviderFactory { - - @Override - default Map getTypeMetadata() { - return Collections.singletonMap("algorithmType", AlgorithmType.RSA); - } + String EC = "EC"; + String RSA = "RSA"; + String OCT = "OCT"; } diff --git a/server-spi-private/src/main/java/org/keycloak/keys/HmacKeyProviderFactory.java b/core/src/main/java/org/keycloak/crypto/KeyUse.java similarity index 60% rename from server-spi-private/src/main/java/org/keycloak/keys/HmacKeyProviderFactory.java rename to core/src/main/java/org/keycloak/crypto/KeyUse.java index 2e91c4c9d996..c5256f7e7370 100644 --- a/server-spi-private/src/main/java/org/keycloak/keys/HmacKeyProviderFactory.java +++ b/core/src/main/java/org/keycloak/crypto/KeyUse.java @@ -14,22 +14,11 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +package org.keycloak.crypto; -package org.keycloak.keys; +public enum KeyUse { -import org.keycloak.jose.jws.AlgorithmType; - -import java.util.Collections; -import java.util.Map; - -/** - * @author Stian Thorgersen - */ -public interface HmacKeyProviderFactory extends KeyProviderFactory { - - @Override - default Map getTypeMetadata() { - return Collections.singletonMap("algorithmType", AlgorithmType.HMAC); - } + SIG, + ENC } diff --git a/core/src/main/java/org/keycloak/crypto/KeyWrapper.java b/core/src/main/java/org/keycloak/crypto/KeyWrapper.java new file mode 100644 index 000000000000..2a592aa44f48 --- /dev/null +++ b/core/src/main/java/org/keycloak/crypto/KeyWrapper.java @@ -0,0 +1,135 @@ +/* + * Copyright 2016 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.keycloak.crypto; + +import javax.crypto.SecretKey; +import java.security.Key; +import java.security.cert.X509Certificate; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; + +public class KeyWrapper { + + private String providerId; + private long providerPriority; + private String kid; + private Set algorithms; + private String type; + private KeyUse use; + private KeyStatus status; + private SecretKey secretKey; + private Key signKey; + private Key verifyKey; + private X509Certificate certificate; + + public String getProviderId() { + return providerId; + } + + public void setProviderId(String providerId) { + this.providerId = providerId; + } + + public long getProviderPriority() { + return providerPriority; + } + + public void setProviderPriority(long providerPriority) { + this.providerPriority = providerPriority; + } + + public String getKid() { + return kid; + } + + public void setKid(String kid) { + this.kid = kid; + } + + public Set getAlgorithms() { + return algorithms; + } + + public void setAlgorithms(String... algorithms) { + this.algorithms = new HashSet<>(); + for (String a : algorithms) { + this.algorithms.add(a); + } + } + + public void setAlgorithms(Set algorithms) { + this.algorithms = algorithms; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public KeyUse getUse() { + return use; + } + + public void setUse(KeyUse use) { + this.use = use; + } + + public KeyStatus getStatus() { + return status; + } + + public void setStatus(KeyStatus status) { + this.status = status; + } + + public SecretKey getSecretKey() { + return secretKey; + } + + public void setSecretKey(SecretKey secretKey) { + this.secretKey = secretKey; + } + + public Key getSignKey() { + return signKey; + } + + public void setSignKey(Key signKey) { + this.signKey = signKey; + } + + public Key getVerifyKey() { + return verifyKey; + } + + public void setVerifyKey(Key verifyKey) { + this.verifyKey = verifyKey; + } + + public X509Certificate getCertificate() { + return certificate; + } + + public void setCertificate(X509Certificate certificate) { + this.certificate = certificate; + } +} diff --git a/core/src/main/java/org/keycloak/representations/idm/KeysMetadataRepresentation.java b/core/src/main/java/org/keycloak/representations/idm/KeysMetadataRepresentation.java index 54d3ba93857d..e70c27370d77 100644 --- a/core/src/main/java/org/keycloak/representations/idm/KeysMetadataRepresentation.java +++ b/core/src/main/java/org/keycloak/representations/idm/KeysMetadataRepresentation.java @@ -19,6 +19,7 @@ import java.util.List; import java.util.Map; +import java.util.Set; /** * @author Stian Thorgersen @@ -54,6 +55,7 @@ public static class KeyMetadataRepresentation { private String status; private String type; + private Set algorithms; private String publicKey; private String certificate; @@ -98,6 +100,14 @@ public void setType(String type) { this.type = type; } + public Set getAlgorithms() { + return algorithms; + } + + public void setAlgorithms(Set algorithms) { + this.algorithms = algorithms; + } + public String getPublicKey() { return publicKey; } diff --git a/server-spi-private/src/main/java/org/keycloak/keys/AesKeyProviderFactory.java b/server-spi-private/src/main/java/org/keycloak/keys/AesKeyProviderFactory.java deleted file mode 100644 index 4c359636b679..000000000000 --- a/server-spi-private/src/main/java/org/keycloak/keys/AesKeyProviderFactory.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright 2017 Red Hat, Inc. and/or its affiliates - * and other contributors as indicated by the @author tags. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.keycloak.keys; - -import java.util.Collections; -import java.util.Map; - -import org.keycloak.jose.jws.AlgorithmType; - -/** - * @author Marek Posolda - */ -public interface AesKeyProviderFactory extends KeyProviderFactory { - - @Override - default Map getTypeMetadata() { - return Collections.singletonMap("algorithmType", AlgorithmType.AES); - } -} diff --git a/server-spi-private/src/main/java/org/keycloak/keys/KeyProvider.java b/server-spi-private/src/main/java/org/keycloak/keys/KeyProvider.java index fb9e152d0c36..7984ea604b2e 100644 --- a/server-spi-private/src/main/java/org/keycloak/keys/KeyProvider.java +++ b/server-spi-private/src/main/java/org/keycloak/keys/KeyProvider.java @@ -17,6 +17,7 @@ package org.keycloak.keys; +import org.keycloak.crypto.KeyWrapper; import org.keycloak.jose.jws.AlgorithmType; import org.keycloak.provider.Provider; @@ -28,26 +29,15 @@ /** * @author Stian Thorgersen */ -public interface KeyProvider extends Provider { +public interface KeyProvider extends Provider { /** - * Returns the algorithm type the keys can be used for - * + * Returns the key * @return */ - AlgorithmType getType(); + List getKeys(); - /** - * Return the KID for the active keypair, or null if no active key is available. - * - * @return - */ - String getKid(); - - /** - * Return metadata about all keypairs held by the provider - * @return - */ - List getKeyMetadata(); + default void close() { + } } diff --git a/server-spi-private/src/main/java/org/keycloak/keys/KeyProviderFactory.java b/server-spi-private/src/main/java/org/keycloak/keys/KeyProviderFactory.java index be005b807e54..3c8766c8db73 100644 --- a/server-spi-private/src/main/java/org/keycloak/keys/KeyProviderFactory.java +++ b/server-spi-private/src/main/java/org/keycloak/keys/KeyProviderFactory.java @@ -17,9 +17,11 @@ package org.keycloak.keys; +import org.keycloak.Config; import org.keycloak.component.ComponentFactory; import org.keycloak.component.ComponentModel; import org.keycloak.models.KeycloakSession; +import org.keycloak.models.KeycloakSessionFactory; /** * @author Stian Thorgersen @@ -28,4 +30,16 @@ public interface KeyProviderFactory extends ComponentFact T create(KeycloakSession session, ComponentModel model); + @Override + default void init(Config.Scope config) { + } + + @Override + default void postInit(KeycloakSessionFactory factory) { + } + + @Override + default void close() { + } + } diff --git a/server-spi-private/src/main/java/org/keycloak/keys/RsaKeyProvider.java b/server-spi-private/src/main/java/org/keycloak/keys/RsaKeyProvider.java deleted file mode 100644 index 6e61c2813b4c..000000000000 --- a/server-spi-private/src/main/java/org/keycloak/keys/RsaKeyProvider.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright 2016 Red Hat, Inc. and/or its affiliates - * and other contributors as indicated by the @author tags. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.keycloak.keys; - -import org.keycloak.jose.jws.AlgorithmType; -import org.keycloak.provider.Provider; - -import java.security.PrivateKey; -import java.security.PublicKey; -import java.security.cert.X509Certificate; -import java.util.List; - -/** - * @author Stian Thorgersen - */ -public interface RsaKeyProvider extends KeyProvider { - - default AlgorithmType getType() { - return AlgorithmType.RSA; - } - - /** - * Return the private key for the active keypair, or null if no active key is available. - * - * @return - */ - PrivateKey getPrivateKey(); - - /** - * Return the public key for the specified kid, or null if the kid is unknown. - * - * @param kid - * @return - */ - PublicKey getPublicKey(String kid); - - /** - * Return the certificate for the specified kid, or null if the kid is unknown. - * - * @param kid - * @return - */ - X509Certificate getCertificate(String kid); - -} diff --git a/server-spi-private/src/main/java/org/keycloak/keys/SecretKeyProvider.java b/server-spi-private/src/main/java/org/keycloak/keys/SecretKeyProvider.java deleted file mode 100644 index a2b25a1c232f..000000000000 --- a/server-spi-private/src/main/java/org/keycloak/keys/SecretKeyProvider.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright 2017 Red Hat, Inc. and/or its affiliates - * and other contributors as indicated by the @author tags. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.keycloak.keys; - -import javax.crypto.SecretKey; - -/** - * Base for secret key providers (HMAC, AES) - * - * @author Marek Posolda - */ -public interface SecretKeyProvider extends KeyProvider { - - /** - * Return the active secret key, or null if no active key is available. - * - * @return - */ - SecretKey getSecretKey(); - - /** - * Return the secret key for the specified kid, or null if the kid is unknown. - * - * @param kid - * @return - */ - SecretKey getSecretKey(String kid); - - - /** - * Return name of Java (JCA) algorithm of the key. For example: HmacSHA256 - * @return - */ - String getJavaAlgorithmName(); -} diff --git a/server-spi/src/main/java/org/keycloak/keys/KeyMetadata.java b/server-spi/src/main/java/org/keycloak/keys/KeyMetadata.java index c9adebd80b5c..296a41324496 100644 --- a/server-spi/src/main/java/org/keycloak/keys/KeyMetadata.java +++ b/server-spi/src/main/java/org/keycloak/keys/KeyMetadata.java @@ -17,21 +17,19 @@ package org.keycloak.keys; +import org.keycloak.crypto.KeyStatus; + /** * @author Stian Thorgersen */ public abstract class KeyMetadata { - public enum Status { - ACTIVE, PASSIVE, DISABLED - } - private String providerId; private long providerPriority; private String kid; - private Status status; + private KeyStatus status; public String getProviderId() { return providerId; @@ -57,11 +55,11 @@ public void setKid(String kid) { this.kid = kid; } - public Status getStatus() { + public KeyStatus getStatus() { return status; } - public void setStatus(Status status) { + public void setStatus(KeyStatus status) { this.status = status; } diff --git a/server-spi/src/main/java/org/keycloak/models/KeyManager.java b/server-spi/src/main/java/org/keycloak/models/KeyManager.java index bc47dcbb4923..f7a9b407e74f 100644 --- a/server-spi/src/main/java/org/keycloak/models/KeyManager.java +++ b/server-spi/src/main/java/org/keycloak/models/KeyManager.java @@ -17,6 +17,8 @@ package org.keycloak.models; +import org.keycloak.crypto.KeyUse; +import org.keycloak.crypto.KeyWrapper; import org.keycloak.keys.SecretKeyMetadata; import org.keycloak.keys.RsaKeyMetadata; @@ -32,25 +34,43 @@ */ public interface KeyManager { + KeyWrapper getActiveKey(RealmModel realm, KeyUse use, String algorithm); + + KeyWrapper getKey(RealmModel realm, String kid, KeyUse use, String algorithm); + + List getKeys(RealmModel realm); + + List getKeys(RealmModel realm, KeyUse use, String algorithm); + + @Deprecated ActiveRsaKey getActiveRsaKey(RealmModel realm); + @Deprecated PublicKey getRsaPublicKey(RealmModel realm, String kid); + @Deprecated Certificate getRsaCertificate(RealmModel realm, String kid); - List getRsaKeys(RealmModel realm, boolean includeDisabled); + @Deprecated + List getRsaKeys(RealmModel realm); + @Deprecated ActiveHmacKey getActiveHmacKey(RealmModel realm); + @Deprecated SecretKey getHmacSecretKey(RealmModel realm, String kid); - List getHmacKeys(RealmModel realm, boolean includeDisabled); + @Deprecated + List getHmacKeys(RealmModel realm); + @Deprecated ActiveAesKey getActiveAesKey(RealmModel realm); + @Deprecated SecretKey getAesSecretKey(RealmModel realm, String kid); - List getAesKeys(RealmModel realm, boolean includeDisabled); + @Deprecated + List getAesKeys(RealmModel realm); class ActiveRsaKey { private final String kid; diff --git a/services/src/main/java/org/keycloak/broker/saml/SAMLIdentityProvider.java b/services/src/main/java/org/keycloak/broker/saml/SAMLIdentityProvider.java index 83a3756469c4..5a9a4c7390fe 100755 --- a/services/src/main/java/org/keycloak/broker/saml/SAMLIdentityProvider.java +++ b/services/src/main/java/org/keycloak/broker/saml/SAMLIdentityProvider.java @@ -17,33 +17,25 @@ package org.keycloak.broker.saml; import org.jboss.logging.Logger; -import org.keycloak.broker.provider.AbstractIdentityProvider; -import org.keycloak.broker.provider.AuthenticationRequest; -import org.keycloak.broker.provider.BrokeredIdentityContext; -import org.keycloak.broker.provider.IdentityBrokerException; -import org.keycloak.broker.provider.IdentityProviderDataMarshaller; +import org.keycloak.broker.provider.*; import org.keycloak.broker.provider.util.SimpleHttp; import org.keycloak.common.util.PemUtils; +import org.keycloak.crypto.KeyStatus; import org.keycloak.dom.saml.v2.assertion.AssertionType; import org.keycloak.dom.saml.v2.assertion.AuthnStatementType; import org.keycloak.dom.saml.v2.assertion.NameIDType; import org.keycloak.dom.saml.v2.assertion.SubjectType; +import org.keycloak.dom.saml.v2.metadata.KeyTypes; import org.keycloak.dom.saml.v2.protocol.ResponseType; import org.keycloak.events.EventBuilder; import org.keycloak.keys.RsaKeyMetadata; -import org.keycloak.models.FederatedIdentityModel; -import org.keycloak.models.KeyManager; -import org.keycloak.models.KeycloakSession; -import org.keycloak.models.RealmModel; -import org.keycloak.models.UserSessionModel; +import org.keycloak.models.*; import org.keycloak.protocol.saml.JaxrsSAML2BindingBuilder; -import org.keycloak.saml.SAML2AuthnRequestBuilder; -import org.keycloak.saml.SAML2LogoutRequestBuilder; -import org.keycloak.saml.SAML2NameIDPolicyBuilder; -import org.keycloak.saml.SPMetadataDescriptor; -import org.keycloak.saml.SignatureAlgorithm; +import org.keycloak.saml.*; import org.keycloak.saml.common.constants.GeneralConstants; import org.keycloak.saml.common.constants.JBossSAMLURIConstants; +import org.keycloak.saml.processing.core.util.KeycloakKeySamlExtensionGenerator; +import org.keycloak.sessions.AuthenticationSessionModel; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; @@ -52,11 +44,6 @@ import java.security.KeyPair; import java.util.Set; import java.util.TreeSet; -import org.keycloak.dom.saml.v2.metadata.KeyTypes; -import org.keycloak.keys.KeyMetadata; -import org.keycloak.keys.KeyMetadata.Status; -import org.keycloak.saml.processing.core.util.KeycloakKeySamlExtensionGenerator; -import org.keycloak.sessions.AuthenticationSessionModel; /** * @author Pedro Igor @@ -246,12 +233,12 @@ public Response export(UriInfo uriInfo, RealmModel realm, String format) { StringBuilder encryptionKeysString = new StringBuilder(); Set keys = new TreeSet<>((o1, o2) -> o1.getStatus() == o2.getStatus() // Status can be only PASSIVE OR ACTIVE, push PASSIVE to end of list ? (int) (o2.getProviderPriority() - o1.getProviderPriority()) - : (o1.getStatus() == KeyMetadata.Status.PASSIVE ? 1 : -1)); - keys.addAll(session.keys().getRsaKeys(realm, false)); + : (o1.getStatus() == KeyStatus.PASSIVE ? 1 : -1)); + keys.addAll(session.keys().getRsaKeys(realm)); for (RsaKeyMetadata key : keys) { addKeyInfo(signingKeysString, key, KeyTypes.SIGNING.value()); - if (key.getStatus() == Status.ACTIVE) { + if (key.getStatus() == KeyStatus.ACTIVE) { addKeyInfo(encryptionKeysString, key, KeyTypes.ENCRYPTION.value()); } } diff --git a/services/src/main/java/org/keycloak/keys/AbstractRsaKeyProvider.java b/services/src/main/java/org/keycloak/keys/AbstractRsaKeyProvider.java index 0584a75c8d4b..8a715b8600c7 100644 --- a/services/src/main/java/org/keycloak/keys/AbstractRsaKeyProvider.java +++ b/services/src/main/java/org/keycloak/keys/AbstractRsaKeyProvider.java @@ -17,13 +17,12 @@ package org.keycloak.keys; +import org.keycloak.common.util.KeyUtils; import org.keycloak.component.ComponentModel; -import org.keycloak.jose.jws.AlgorithmType; +import org.keycloak.crypto.*; import org.keycloak.models.RealmModel; import java.security.KeyPair; -import java.security.PrivateKey; -import java.security.PublicKey; import java.security.cert.X509Certificate; import java.util.Collections; import java.util.List; @@ -31,110 +30,49 @@ /** * @author Stian Thorgersen */ -public abstract class AbstractRsaKeyProvider implements RsaKeyProvider { +public abstract class AbstractRsaKeyProvider implements KeyProvider { - private final boolean enabled; - - private final boolean active; + private final KeyStatus status; private final ComponentModel model; - private final Keys keys; + private final KeyWrapper key; public AbstractRsaKeyProvider(RealmModel realm, ComponentModel model) { this.model = model; + this.status = KeyStatus.from(model.get(Attributes.ACTIVE_KEY, true), model.get(Attributes.ENABLED_KEY, true)); - this.enabled = model.get(Attributes.ENABLED_KEY, true); - this.active = model.get(Attributes.ACTIVE_KEY, true); - - if (model.hasNote(Keys.class.getName())) { - keys = model.getNote(Keys.class.getName()); + if (model.hasNote(KeyWrapper.class.getName())) { + key = model.getNote(KeyWrapper.class.getName()); } else { - keys = loadKeys(realm, model); - model.setNote(Keys.class.getName(), keys); + key = loadKey(realm, model); + model.setNote(KeyWrapper.class.getName(), key); } } - protected abstract Keys loadKeys(RealmModel realm, ComponentModel model); - - @Override - public final String getKid() { - return isActive() ? keys.getKid() : null; - } - - @Override - public final PrivateKey getPrivateKey() { - return isActive() ? keys.getKeyPair().getPrivate() : null; - } - - @Override - public final PublicKey getPublicKey(String kid) { - return isEnabled() && kid.equals(keys.getKid()) ? keys.getKeyPair().getPublic() : null; - } - - @Override - public X509Certificate getCertificate(String kid) { - return isEnabled() && kid.equals(keys.getKid()) ? keys.getCertificate() : null; - } - - @Override - public final List getKeyMetadata() { - String kid = keys.getKid(); - PublicKey publicKey = keys.getKeyPair().getPublic(); - if (kid != null && publicKey != null) { - RsaKeyMetadata k = new RsaKeyMetadata(); - k.setProviderId(model.getId()); - k.setProviderPriority(model.get(Attributes.PRIORITY_KEY, 0l)); - k.setKid(kid); - if (isActive()) { - k.setStatus(KeyMetadata.Status.ACTIVE); - } else if (isEnabled()) { - k.setStatus(KeyMetadata.Status.PASSIVE); - } else { - k.setStatus(KeyMetadata.Status.DISABLED); - } - k.setPublicKey(publicKey); - k.setCertificate(keys.getCertificate()); - return Collections.singletonList(k); - } else { - return Collections.emptyList(); - } - } + protected abstract KeyWrapper loadKey(RealmModel realm, ComponentModel model); @Override - public void close() { - } - - private boolean isEnabled() { - return keys != null && enabled; + public List getKeys() { + return Collections.singletonList(key); } - private boolean isActive() { - return isEnabled() && active; - } - - public static class Keys { - private String kid; - private KeyPair keyPair; - private X509Certificate certificate; + protected KeyWrapper createKeyWrapper(KeyPair keyPair, X509Certificate certificate) { + KeyWrapper key = new KeyWrapper(); - public Keys(String kid, KeyPair keyPair, X509Certificate certificate) { - this.kid = kid; - this.keyPair = keyPair; - this.certificate = certificate; - } + key.setProviderId(model.getId()); + key.setProviderPriority(model.get("priority", 0l)); - public String getKid() { - return kid; - } + key.setKid(KeyUtils.createKeyId(keyPair.getPublic())); + key.setUse(KeyUse.SIG); + key.setType(KeyType.RSA); + key.setAlgorithms(Algorithm.RS256, Algorithm.RS384, Algorithm.RS512); + key.setStatus(status); + key.setSignKey(keyPair.getPrivate()); + key.setVerifyKey(keyPair.getPublic()); + key.setCertificate(certificate); - public KeyPair getKeyPair() { - return keyPair; - } - - public X509Certificate getCertificate() { - return certificate; - } + return key; } } diff --git a/services/src/main/java/org/keycloak/keys/AbstractRsaKeyProviderFactory.java b/services/src/main/java/org/keycloak/keys/AbstractRsaKeyProviderFactory.java index f20dc2017e9b..1c2af4fcab99 100644 --- a/services/src/main/java/org/keycloak/keys/AbstractRsaKeyProviderFactory.java +++ b/services/src/main/java/org/keycloak/keys/AbstractRsaKeyProviderFactory.java @@ -27,7 +27,7 @@ /** * @author Stian Thorgersen */ -public abstract class AbstractRsaKeyProviderFactory implements RsaKeyProviderFactory { +public abstract class AbstractRsaKeyProviderFactory implements KeyProviderFactory { public final static ProviderConfigurationBuilder configurationBuilder() { return ProviderConfigurationBuilder.create() diff --git a/services/src/main/java/org/keycloak/keys/DefaultKeyManager.java b/services/src/main/java/org/keycloak/keys/DefaultKeyManager.java index 6a332934573e..7ebb1a4e78f6 100644 --- a/services/src/main/java/org/keycloak/keys/DefaultKeyManager.java +++ b/services/src/main/java/org/keycloak/keys/DefaultKeyManager.java @@ -19,20 +19,19 @@ import org.jboss.logging.Logger; import org.keycloak.component.ComponentModel; -import org.keycloak.jose.jws.AlgorithmType; +import org.keycloak.crypto.Algorithm; +import org.keycloak.crypto.KeyUse; +import org.keycloak.crypto.KeyWrapper; import org.keycloak.models.KeyManager; import org.keycloak.models.KeycloakSession; import org.keycloak.models.RealmModel; import org.keycloak.provider.ProviderFactory; import javax.crypto.SecretKey; +import java.security.PrivateKey; import java.security.PublicKey; import java.security.cert.Certificate; -import java.util.Comparator; -import java.util.HashMap; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; +import java.util.*; /** * @author Stian Thorgersen @@ -49,204 +48,172 @@ public DefaultKeyManager(KeycloakSession session) { } @Override - public ActiveRsaKey getActiveRsaKey(RealmModel realm) { + public KeyWrapper getActiveKey(RealmModel realm, KeyUse use, String algorithm) { for (KeyProvider p : getProviders(realm)) { - if (p.getType().equals(AlgorithmType.RSA)) { - RsaKeyProvider r = (RsaKeyProvider) p; - if (r.getKid() != null && r.getPrivateKey() != null) { + for (KeyWrapper key : p .getKeys()) { + if (key.getStatus().isActive() && matches(key, use, algorithm)) { if (logger.isTraceEnabled()) { - logger.tracev("Active key realm={0} kid={1}", realm.getName(), p.getKid()); + logger.tracev("Active key found: realm={0} kid={1} algorithm={2}", realm.getName(), key.getKid(), algorithm); } - String kid = p.getKid(); - return new ActiveRsaKey(kid, r.getPrivateKey(), r.getPublicKey(kid), r.getCertificate(kid)); - } - } - } - throw new RuntimeException("Failed to get RSA keys"); - } - - @Override - public ActiveHmacKey getActiveHmacKey(RealmModel realm) { - for (KeyProvider p : getProviders(realm)) { - if (p.getType().equals(AlgorithmType.HMAC)) { - HmacKeyProvider h = (HmacKeyProvider) p; - if (h.getKid() != null && h.getSecretKey() != null) { - if (logger.isTraceEnabled()) { - logger.tracev("Active secret realm={0} kid={1}", realm.getName(), p.getKid()); - } - String kid = p.getKid(); - return new ActiveHmacKey(kid, h.getSecretKey()); - } - } - } - throw new RuntimeException("Failed to get keys"); - } - @Override - public ActiveAesKey getActiveAesKey(RealmModel realm) { - for (KeyProvider p : getProviders(realm)) { - if (p.getType().equals(AlgorithmType.AES)) { - AesKeyProvider h = (AesKeyProvider) p; - if (h.getKid() != null && h.getSecretKey() != null) { - if (logger.isTraceEnabled()) { - logger.tracev("Active AES Key realm={0} kid={1}", realm.getName(), p.getKid()); - } - String kid = p.getKid(); - return new ActiveAesKey(kid, h.getSecretKey()); + return key; } } } - throw new RuntimeException("Failed to get keys"); + throw new RuntimeException("Failed to find key: realm=" + realm.getName() + " algorithm=" + algorithm); } @Override - public PublicKey getRsaPublicKey(RealmModel realm, String kid) { + public KeyWrapper getKey(RealmModel realm, String kid, KeyUse use, String algorithm) { if (kid == null) { - logger.warnv("KID is null, can't find public key", realm.getName(), kid); + logger.warnv("kid is null, can't find public key", realm.getName(), kid); return null; } for (KeyProvider p : getProviders(realm)) { - if (p.getType().equals(AlgorithmType.RSA)) { - RsaKeyProvider r = (RsaKeyProvider) p; - PublicKey publicKey = r.getPublicKey(kid); - if (publicKey != null) { + for (KeyWrapper key : p.getKeys()) { + if (key.getKid().equals(kid) && key.getStatus().isEnabled() && matches(key, use, algorithm)) { if (logger.isTraceEnabled()) { - logger.tracev("Found public key realm={0} kid={1}", realm.getName(), kid); + logger.tracev("Active key realm={0} kid={1} algorithm={2}", realm.getName(), key.getKid(), algorithm); } - return publicKey; + + return key; } } } + if (logger.isTraceEnabled()) { - logger.tracev("Failed to find public key realm={0} kid={1}", realm.getName(), kid); + logger.tracev("Failed to find public key realm={0} kid={1} algorithm={2}", realm.getName(), kid, algorithm); } + return null; } @Override - public Certificate getRsaCertificate(RealmModel realm, String kid) { - if (kid == null) { - logger.warnv("KID is null, can't find public key", realm.getName(), kid); - return null; - } - + public List getKeys(RealmModel realm, KeyUse use, String algorithm) { + List keys = new LinkedList<>(); for (KeyProvider p : getProviders(realm)) { - if (p.getType().equals(AlgorithmType.RSA)) { - RsaKeyProvider r = (RsaKeyProvider) p; - Certificate certificate = r.getCertificate(kid); - if (certificate != null) { - if (logger.isTraceEnabled()) { - logger.tracev("Found certificate realm={0} kid={1}", realm.getName(), kid); - } - return certificate; + for (KeyWrapper key : p .getKeys()) { + if (key.getStatus().isEnabled() && matches(key, use, algorithm)) { + keys.add(key); } } } - if (logger.isTraceEnabled()) { - logger.tracev("Failed to find certificate realm={0} kid={1}", realm.getName(), kid); - } - return null; + return keys; } @Override - public SecretKey getHmacSecretKey(RealmModel realm, String kid) { - if (kid == null) { - logger.warnv("KID is null, can't find secret key", realm.getName(), kid); - return null; - } - + public List getKeys(RealmModel realm) { + List keys = new LinkedList<>(); for (KeyProvider p : getProviders(realm)) { - if (p.getType().equals(AlgorithmType.HMAC)) { - HmacKeyProvider h = (HmacKeyProvider) p; - SecretKey s = h.getSecretKey(kid); - if (s != null) { - if (logger.isTraceEnabled()) { - logger.tracev("Found secret key realm={0} kid={1}", realm.getName(), kid); - } - return s; - } + for (KeyWrapper key : p .getKeys()) { + keys.add(key); } } - if (logger.isTraceEnabled()) { - logger.tracev("Failed to find secret key realm={0} kid={1}", realm.getName(), kid); - } - return null; + return keys; } @Override - public SecretKey getAesSecretKey(RealmModel realm, String kid) { - if (kid == null) { - logger.warnv("KID is null, can't find aes key", realm.getName(), kid); - return null; - } + @Deprecated + public ActiveRsaKey getActiveRsaKey(RealmModel realm) { + KeyWrapper key = getActiveKey(realm, KeyUse.SIG, Algorithm.RS256); + return new ActiveRsaKey(key.getKid(), (PrivateKey) key.getSignKey(), (PublicKey) key.getVerifyKey(), key.getCertificate()); + } - for (KeyProvider p : getProviders(realm)) { - if (p.getType().equals(AlgorithmType.AES)) { - AesKeyProvider h = (AesKeyProvider) p; - SecretKey s = h.getSecretKey(kid); - if (s != null) { - if (logger.isTraceEnabled()) { - logger.tracev("Found AES key realm={0} kid={1}", realm.getName(), kid); - } - return s; - } - } - } - if (logger.isTraceEnabled()) { - logger.tracev("Failed to find AES key realm={0} kid={1}", realm.getName(), kid); - } - return null; + @Override + @Deprecated + public ActiveHmacKey getActiveHmacKey(RealmModel realm) { + KeyWrapper key = getActiveKey(realm, KeyUse.SIG, Algorithm.HS256); + return new ActiveHmacKey(key.getKid(), key.getSecretKey()); } @Override - public List getRsaKeys(RealmModel realm, boolean includeDisabled) { + @Deprecated + public ActiveAesKey getActiveAesKey(RealmModel realm) { + KeyWrapper key = getActiveKey(realm, KeyUse.ENC, Algorithm.AES); + return new ActiveAesKey(key.getKid(), key.getSecretKey()); + } + + @Override + @Deprecated + public PublicKey getRsaPublicKey(RealmModel realm, String kid) { + KeyWrapper key = getKey(realm, kid, KeyUse.SIG, Algorithm.RS256); + return key != null ? (PublicKey) key.getVerifyKey() : null; + } + + @Override + @Deprecated + public Certificate getRsaCertificate(RealmModel realm, String kid) { + KeyWrapper key = getKey(realm, kid, KeyUse.SIG, Algorithm.RS256); + return key != null ? key.getCertificate() : null; + } + + @Override + @Deprecated + public SecretKey getHmacSecretKey(RealmModel realm, String kid) { + KeyWrapper key = getKey(realm, kid, KeyUse.SIG, Algorithm.HS256); + return key != null ? key.getSecretKey() : null; + } + + @Override + @Deprecated + public SecretKey getAesSecretKey(RealmModel realm, String kid) { + KeyWrapper key = getKey(realm, kid, KeyUse.ENC, Algorithm.AES); + return key.getSecretKey(); + } + + @Override + @Deprecated + public List getRsaKeys(RealmModel realm) { List keys = new LinkedList<>(); - for (KeyProvider p : getProviders(realm)) { - if (p instanceof RsaKeyProvider) { - if (includeDisabled) { - keys.addAll(p.getKeyMetadata()); - } else { - List metadata = p.getKeyMetadata(); - metadata.stream().filter(k -> k.getStatus() != KeyMetadata.Status.DISABLED).forEach(k -> keys.add(k)); - } - } + for (KeyWrapper key : getKeys(realm, KeyUse.SIG, Algorithm.RS256)) { + RsaKeyMetadata m = new RsaKeyMetadata(); + m.setCertificate(key.getCertificate()); + m.setPublicKey((PublicKey) key.getVerifyKey()); + m.setKid(key.getKid()); + m.setProviderId(key.getProviderId()); + m.setProviderPriority(key.getProviderPriority()); + m.setStatus(key.getStatus()); + + keys.add(m); } return keys; } @Override - public List getHmacKeys(RealmModel realm, boolean includeDisabled) { + public List getHmacKeys(RealmModel realm) { List keys = new LinkedList<>(); - for (KeyProvider p : getProviders(realm)) { - if (p instanceof HmacKeyProvider) { - if (includeDisabled) { - keys.addAll(p.getKeyMetadata()); - } else { - List metadata = p.getKeyMetadata(); - metadata.stream().filter(k -> k.getStatus() != KeyMetadata.Status.DISABLED).forEach(k -> keys.add(k)); - } - } + for (KeyWrapper key : getKeys(realm, KeyUse.SIG, Algorithm.HS256)) { + SecretKeyMetadata m = new SecretKeyMetadata(); + m.setKid(key.getKid()); + m.setProviderId(key.getProviderId()); + m.setProviderPriority(key.getProviderPriority()); + m.setStatus(key.getStatus()); + + keys.add(m); } return keys; } @Override - public List getAesKeys(RealmModel realm, boolean includeDisabled) { + public List getAesKeys(RealmModel realm) { List keys = new LinkedList<>(); - for (KeyProvider p : getProviders(realm)) { - if (p instanceof AesKeyProvider) { - if (includeDisabled) { - keys.addAll(p.getKeyMetadata()); - } else { - List metadata = p.getKeyMetadata(); - metadata.stream().filter(k -> k.getStatus() != KeyMetadata.Status.DISABLED).forEach(k -> keys.add(k)); - } - } + for (KeyWrapper key : getKeys(realm, KeyUse.ENC, Algorithm.AES)) { + SecretKeyMetadata m = new SecretKeyMetadata(); + m.setKid(key.getKid()); + m.setProviderId(key.getProviderId()); + m.setProviderPriority(key.getProviderPriority()); + m.setStatus(key.getStatus()); + + keys.add(m); } return keys; } + private boolean matches(KeyWrapper key, KeyUse use, String algorithm) { + return use.equals(key.getUse()) && key.getAlgorithms().contains(algorithm); + } + private List getProviders(RealmModel realm) { List providers = providersMap.get(realm.getId()); if (providers == null) { @@ -255,10 +222,6 @@ private List getProviders(RealmModel realm) { List components = new LinkedList<>(realm.getComponents(realm.getId(), KeyProvider.class.getName())); components.sort(new ProviderComparator()); - boolean activeRsa = false; - boolean activeHmac = false; - boolean activeAes = false; - for (ComponentModel c : components) { try { ProviderFactory f = session.getKeycloakSessionFactory().getProviderFactory(KeyProvider.class, c.getProviderId()); @@ -266,41 +229,30 @@ private List getProviders(RealmModel realm) { KeyProvider provider = factory.create(session, c); session.enlistForClose(provider); providers.add(provider); - if (provider.getType().equals(AlgorithmType.RSA)) { - RsaKeyProvider r = (RsaKeyProvider) provider; - if (r.getKid() != null && r.getPrivateKey() != null) { - activeRsa = true; - } - } else if (provider.getType().equals(AlgorithmType.HMAC)) { - HmacKeyProvider r = (HmacKeyProvider) provider; - if (r.getKid() != null && r.getSecretKey() != null) { - activeHmac = true; - } - } else if (provider.getType().equals(AlgorithmType.AES)) { - AesKeyProvider r = (AesKeyProvider) provider; - if (r.getKid() != null && r.getSecretKey() != null) { - activeAes = true; - } - } - } catch (Throwable t) { logger.errorv(t, "Failed to load provider {0}", c.getId()); } } - if (!activeRsa) { + providersMap.put(realm.getId(), providers); + + try { + getActiveKey(realm, KeyUse.SIG, Algorithm.RS256); + } catch (RuntimeException e) { providers.add(new FailsafeRsaKeyProvider()); } - if (!activeHmac) { + try { + getActiveKey(realm, KeyUse.SIG, Algorithm.HS256); + } catch (RuntimeException e) { providers.add(new FailsafeHmacKeyProvider()); } - if (!activeAes) { + try { + getActiveKey(realm, KeyUse.ENC, Algorithm.AES); + } catch (RuntimeException e) { providers.add(new FailsafeAesKeyProvider()); } - - providersMap.put(realm.getId(), providers); } return providers; } diff --git a/services/src/main/java/org/keycloak/keys/FailsafeAesKeyProvider.java b/services/src/main/java/org/keycloak/keys/FailsafeAesKeyProvider.java index d81a5965b113..bac9f761714d 100644 --- a/services/src/main/java/org/keycloak/keys/FailsafeAesKeyProvider.java +++ b/services/src/main/java/org/keycloak/keys/FailsafeAesKeyProvider.java @@ -18,14 +18,32 @@ package org.keycloak.keys; import org.jboss.logging.Logger; +import org.keycloak.crypto.Algorithm; +import org.keycloak.crypto.KeyType; +import org.keycloak.crypto.KeyUse; /** * @author Marek Posolda */ -public class FailsafeAesKeyProvider extends FailsafeSecretKeyProvider implements AesKeyProvider { +public class FailsafeAesKeyProvider extends FailsafeSecretKeyProvider { private static final Logger logger = Logger.getLogger(FailsafeAesKeyProvider.class); + @Override + protected KeyUse getUse() { + return KeyUse.ENC; + } + + @Override + protected String getType() { + return KeyType.OCT; + } + + @Override + protected String getAlgorithm() { + return Algorithm.AES; + } + @Override protected Logger logger() { return logger; diff --git a/services/src/main/java/org/keycloak/keys/FailsafeHmacKeyProvider.java b/services/src/main/java/org/keycloak/keys/FailsafeHmacKeyProvider.java index 676e04807430..1114c24a392a 100644 --- a/services/src/main/java/org/keycloak/keys/FailsafeHmacKeyProvider.java +++ b/services/src/main/java/org/keycloak/keys/FailsafeHmacKeyProvider.java @@ -20,6 +20,10 @@ import org.jboss.logging.Logger; import org.keycloak.common.util.KeyUtils; import org.keycloak.common.util.Time; +import org.keycloak.crypto.Algorithm; +import org.keycloak.crypto.KeyType; +import org.keycloak.crypto.KeyUse; +import org.keycloak.crypto.KeyWrapper; import org.keycloak.models.utils.KeycloakModelUtils; import javax.crypto.SecretKey; @@ -29,12 +33,28 @@ /** * @author Stian Thorgersen */ -public class FailsafeHmacKeyProvider extends FailsafeSecretKeyProvider implements HmacKeyProvider { +public class FailsafeHmacKeyProvider extends FailsafeSecretKeyProvider { private static final Logger logger = Logger.getLogger(FailsafeHmacKeyProvider.class); + @Override + protected KeyUse getUse() { + return KeyUse.SIG; + } + + @Override + protected String getType() { + return KeyType.OCT; + } + + @Override + protected String getAlgorithm() { + return Algorithm.HS256; + } + @Override protected Logger logger() { return logger; } + } diff --git a/services/src/main/java/org/keycloak/keys/FailsafeRsaKeyProvider.java b/services/src/main/java/org/keycloak/keys/FailsafeRsaKeyProvider.java index 8515f29b7249..9af84a5f128a 100644 --- a/services/src/main/java/org/keycloak/keys/FailsafeRsaKeyProvider.java +++ b/services/src/main/java/org/keycloak/keys/FailsafeRsaKeyProvider.java @@ -20,77 +20,62 @@ import org.jboss.logging.Logger; import org.keycloak.common.util.KeyUtils; import org.keycloak.common.util.Time; +import org.keycloak.crypto.*; import java.security.KeyPair; -import java.security.PrivateKey; -import java.security.PublicKey; -import java.security.cert.X509Certificate; import java.util.Collections; import java.util.List; /** * @author Stian Thorgersen */ -public class FailsafeRsaKeyProvider implements RsaKeyProvider { +public class FailsafeRsaKeyProvider implements KeyProvider { private static final Logger logger = Logger.getLogger(FailsafeRsaKeyProvider.class); - private static String KID; - - private static KeyPair KEY_PAIR; + private static KeyWrapper KEY; private static long EXPIRES; - private KeyPair keyPair; - - private String kid; + private KeyWrapper key; public FailsafeRsaKeyProvider() { logger.errorv("No active keys found, using failsafe provider, please login to admin console to add keys. Clustering is not supported."); synchronized (FailsafeRsaKeyProvider.class) { if (EXPIRES < Time.currentTime()) { - KEY_PAIR = KeyUtils.generateRsaKeyPair(2048); - KID = KeyUtils.createKeyId(KEY_PAIR.getPublic()); + KEY = createKeyWrapper(); EXPIRES = Time.currentTime() + 60 * 10; if (EXPIRES > 0) { - logger.warnv("Keys expired, re-generated kid={0}", KID); + logger.warnv("Keys expired, re-generated kid={0}", KEY.getKid()); } } - kid = KID; - keyPair = KEY_PAIR; + key = KEY; } } @Override - public String getKid() { - return kid; + public List getKeys() { + return Collections.singletonList(key); } - @Override - public PrivateKey getPrivateKey() { - return keyPair.getPrivate(); - } + private KeyWrapper createKeyWrapper() { + KeyPair keyPair = KeyUtils.generateRsaKeyPair(2048); - @Override - public PublicKey getPublicKey(String kid) { - return kid.equals(this.kid) ? keyPair.getPublic() : null; - } + KeyWrapper key = new KeyWrapper(); - @Override - public X509Certificate getCertificate(String kid) { - return null; - } + key.setKid(KeyUtils.createKeyId(keyPair.getPublic())); + key.setUse(KeyUse.SIG); + key.setType(KeyType.RSA); + key.setAlgorithms(Algorithm.RS256, Algorithm.RS384, Algorithm.RS512); + key.setStatus(KeyStatus.ACTIVE); + key.setSignKey(keyPair.getPrivate()); + key.setVerifyKey(keyPair.getPublic()); - @Override - public List getKeyMetadata() { - return Collections.emptyList(); + return key; } - @Override - public void close() { - } } diff --git a/services/src/main/java/org/keycloak/keys/FailsafeSecretKeyProvider.java b/services/src/main/java/org/keycloak/keys/FailsafeSecretKeyProvider.java index 32be2637126b..2cb565d06582 100644 --- a/services/src/main/java/org/keycloak/keys/FailsafeSecretKeyProvider.java +++ b/services/src/main/java/org/keycloak/keys/FailsafeSecretKeyProvider.java @@ -17,74 +17,72 @@ package org.keycloak.keys; -import java.util.Collections; -import java.util.List; - -import javax.crypto.SecretKey; - import org.jboss.logging.Logger; import org.keycloak.common.util.KeyUtils; import org.keycloak.common.util.Time; +import org.keycloak.crypto.JavaAlgorithm; +import org.keycloak.crypto.KeyStatus; +import org.keycloak.crypto.KeyUse; +import org.keycloak.crypto.KeyWrapper; import org.keycloak.models.utils.KeycloakModelUtils; +import javax.crypto.SecretKey; +import java.util.Collections; +import java.util.List; + /** * @author Stian Thorgersen */ -public abstract class FailsafeSecretKeyProvider implements SecretKeyProvider { - - - private static String KID; +public abstract class FailsafeSecretKeyProvider implements KeyProvider { - private static SecretKey KEY; + private static KeyWrapper KEY; private static long EXPIRES; - private SecretKey key; - - private String kid; + private KeyWrapper key; public FailsafeSecretKeyProvider() { logger().errorv("No active keys found, using failsafe provider, please login to admin console to add keys. Clustering is not supported."); synchronized (FailsafeHmacKeyProvider.class) { if (EXPIRES < Time.currentTime()) { - KEY = KeyUtils.loadSecretKey(KeycloakModelUtils.generateSecret(32), getJavaAlgorithmName()); - KID = KeycloakModelUtils.generateId(); + KEY = createKeyWrapper(); EXPIRES = Time.currentTime() + 60 * 10; if (EXPIRES > 0) { - logger().warnv("Keys expired, re-generated kid={0}", KID); + logger().warnv("Keys expired, re-generated kid={0}", KEY.getKid()); } } - kid = KID; key = KEY; } } @Override - public String getKid() { - return kid; + public List getKeys() { + return Collections.singletonList(key); } - @Override - public SecretKey getSecretKey() { + private KeyWrapper createKeyWrapper() { + SecretKey secretKey = KeyUtils.loadSecretKey(KeycloakModelUtils.generateSecret(32), JavaAlgorithm.getJavaAlgorithm(getAlgorithm())); + + KeyWrapper key = new KeyWrapper(); + + key.setKid(KeycloakModelUtils.generateId()); + key.setUse(getUse()); + key.setType(getType()); + key.setAlgorithms(getAlgorithm()); + key.setStatus(KeyStatus.ACTIVE); + key.setSecretKey(secretKey); + return key; } - @Override - public SecretKey getSecretKey(String kid) { - return kid.equals(this.kid) ? key : null; - } + protected abstract KeyUse getUse(); - @Override - public List getKeyMetadata() { - return Collections.emptyList(); - } + protected abstract String getType(); - @Override - public void close() { - } + protected abstract String getAlgorithm(); protected abstract Logger logger(); } diff --git a/services/src/main/java/org/keycloak/keys/GeneratedAesKeyProvider.java b/services/src/main/java/org/keycloak/keys/GeneratedAesKeyProvider.java index cb07323cf604..5a25f3148173 100644 --- a/services/src/main/java/org/keycloak/keys/GeneratedAesKeyProvider.java +++ b/services/src/main/java/org/keycloak/keys/GeneratedAesKeyProvider.java @@ -18,14 +18,17 @@ package org.keycloak.keys; import org.keycloak.component.ComponentModel; +import org.keycloak.crypto.Algorithm; +import org.keycloak.crypto.KeyType; +import org.keycloak.crypto.KeyUse; /** * @author Marek Posolda */ -public class GeneratedAesKeyProvider extends GeneratedSecretKeyProvider implements AesKeyProvider { +public class GeneratedAesKeyProvider extends GeneratedSecretKeyProvider implements KeyProvider { public GeneratedAesKeyProvider(ComponentModel model) { - super(model); + super(model, KeyUse.ENC, KeyType.OCT, Algorithm.AES); } } diff --git a/services/src/main/java/org/keycloak/keys/GeneratedAesKeyProviderFactory.java b/services/src/main/java/org/keycloak/keys/GeneratedAesKeyProviderFactory.java index b9f5a06ba015..e8974aa18574 100644 --- a/services/src/main/java/org/keycloak/keys/GeneratedAesKeyProviderFactory.java +++ b/services/src/main/java/org/keycloak/keys/GeneratedAesKeyProviderFactory.java @@ -21,6 +21,7 @@ import org.jboss.logging.Logger; import org.keycloak.component.ComponentModel; +import org.keycloak.crypto.KeyUse; import org.keycloak.models.KeycloakSession; import org.keycloak.provider.ProviderConfigProperty; @@ -29,7 +30,7 @@ /** * @author Marek Posolda */ -public class GeneratedAesKeyProviderFactory extends GeneratedSecretKeyProviderFactory implements AesKeyProviderFactory { +public class GeneratedAesKeyProviderFactory extends GeneratedSecretKeyProviderFactory { private static final Logger logger = Logger.getLogger(GeneratedAesKeyProviderFactory.class); @@ -52,7 +53,7 @@ public class GeneratedAesKeyProviderFactory extends GeneratedSecretKeyProviderFa .build(); @Override - public AesKeyProvider create(KeycloakSession session, ComponentModel model) { + public GeneratedAesKeyProvider create(KeycloakSession session, ComponentModel model) { return new GeneratedAesKeyProvider(model); } diff --git a/services/src/main/java/org/keycloak/keys/GeneratedHmacKeyProvider.java b/services/src/main/java/org/keycloak/keys/GeneratedHmacKeyProvider.java index dfc8fb27b132..2367a16bf833 100644 --- a/services/src/main/java/org/keycloak/keys/GeneratedHmacKeyProvider.java +++ b/services/src/main/java/org/keycloak/keys/GeneratedHmacKeyProvider.java @@ -18,15 +18,18 @@ package org.keycloak.keys; import org.keycloak.component.ComponentModel; +import org.keycloak.crypto.Algorithm; +import org.keycloak.crypto.KeyType; +import org.keycloak.crypto.KeyUse; /** * @author Stian Thorgersen */ -public class GeneratedHmacKeyProvider extends GeneratedSecretKeyProvider implements HmacKeyProvider { +public class GeneratedHmacKeyProvider extends GeneratedSecretKeyProvider { public GeneratedHmacKeyProvider(ComponentModel model) { - super(model); + super(model, KeyUse.SIG, KeyType.OCT, Algorithm.HS256); } } diff --git a/services/src/main/java/org/keycloak/keys/GeneratedHmacKeyProviderFactory.java b/services/src/main/java/org/keycloak/keys/GeneratedHmacKeyProviderFactory.java index ab4902ade751..9f588769c984 100644 --- a/services/src/main/java/org/keycloak/keys/GeneratedHmacKeyProviderFactory.java +++ b/services/src/main/java/org/keycloak/keys/GeneratedHmacKeyProviderFactory.java @@ -34,7 +34,7 @@ /** * @author Stian Thorgersen */ -public class GeneratedHmacKeyProviderFactory extends GeneratedSecretKeyProviderFactory implements HmacKeyProviderFactory { +public class GeneratedHmacKeyProviderFactory extends GeneratedSecretKeyProviderFactory { private static final Logger logger = Logger.getLogger(GeneratedHmacKeyProviderFactory.class); @@ -49,7 +49,7 @@ public class GeneratedHmacKeyProviderFactory extends GeneratedSecretKeyProviderF .build(); @Override - public HmacKeyProvider create(KeycloakSession session, ComponentModel model) { + public GeneratedHmacKeyProvider create(KeycloakSession session, ComponentModel model) { return new GeneratedHmacKeyProvider(model); } diff --git a/services/src/main/java/org/keycloak/keys/GeneratedRsaKeyProviderFactory.java b/services/src/main/java/org/keycloak/keys/GeneratedRsaKeyProviderFactory.java index a0a5e87b5bd6..4aa10dafecf3 100644 --- a/services/src/main/java/org/keycloak/keys/GeneratedRsaKeyProviderFactory.java +++ b/services/src/main/java/org/keycloak/keys/GeneratedRsaKeyProviderFactory.java @@ -110,18 +110,6 @@ public List getConfigProperties() { return CONFIG_PROPERTIES; } - @Override - public void init(Config.Scope config) { - } - - @Override - public void postInit(KeycloakSessionFactory factory) { - } - - @Override - public void close() { - } - @Override public String getId() { return ID; diff --git a/services/src/main/java/org/keycloak/keys/GeneratedSecretKeyProvider.java b/services/src/main/java/org/keycloak/keys/GeneratedSecretKeyProvider.java index 76b7ea82902a..1d416d28b586 100644 --- a/services/src/main/java/org/keycloak/keys/GeneratedSecretKeyProvider.java +++ b/services/src/main/java/org/keycloak/keys/GeneratedSecretKeyProvider.java @@ -17,86 +17,67 @@ package org.keycloak.keys; -import java.util.Collections; -import java.util.List; - -import javax.crypto.SecretKey; - import org.keycloak.common.util.Base64Url; import org.keycloak.common.util.KeyUtils; import org.keycloak.component.ComponentModel; +import org.keycloak.crypto.JavaAlgorithm; +import org.keycloak.crypto.KeyStatus; +import org.keycloak.crypto.KeyUse; +import org.keycloak.crypto.KeyWrapper; +import org.keycloak.models.utils.KeycloakModelUtils; + +import javax.crypto.SecretKey; +import java.util.Collections; +import java.util.List; /** * @author Stian Thorgersen */ -public abstract class GeneratedSecretKeyProvider implements SecretKeyProvider { - - private final boolean enabled; - - private final boolean active; +public abstract class GeneratedSecretKeyProvider implements KeyProvider { + private final KeyStatus status; private final ComponentModel model; private final String kid; private final SecretKey secretKey; + private final KeyUse use; + private String type; + private final String algorithm; - public GeneratedSecretKeyProvider(ComponentModel model) { - this.enabled = model.get(Attributes.ENABLED_KEY, true); - this.active = model.get(Attributes.ACTIVE_KEY, true); + public GeneratedSecretKeyProvider(ComponentModel model, KeyUse use, String type, String algorithm) { + this.status = KeyStatus.from(model.get(Attributes.ACTIVE_KEY, true), model.get(Attributes.ENABLED_KEY, true)); this.kid = model.get(Attributes.KID_KEY); this.model = model; + this.use = use; + this.type = type; + this.algorithm = algorithm; if (model.hasNote(SecretKey.class.getName())) { secretKey = model.getNote(SecretKey.class.getName()); } else { - secretKey = KeyUtils.loadSecretKey(Base64Url.decode(model.get(Attributes.SECRET_KEY)), getJavaAlgorithmName()); + secretKey = KeyUtils.loadSecretKey(Base64Url.decode(model.get(Attributes.SECRET_KEY)), JavaAlgorithm.getJavaAlgorithm(algorithm)); model.setNote(SecretKey.class.getName(), secretKey); } } @Override - public SecretKey getSecretKey() { - return isActive() ? secretKey : null; - } + public List getKeys() { + KeyWrapper key = new KeyWrapper(); - @Override - public SecretKey getSecretKey(String kid) { - return isEnabled() && kid.equals(this.kid) ? secretKey : null; - } + key.setProviderId(model.getId()); + key.setProviderPriority(model.get("priority", 0l)); - @Override - public String getKid() { - return isActive() ? kid : null; - } + key.setKid(kid); + key.setUse(use); + key.setType(type); + key.setAlgorithms(algorithm); + key.setStatus(status); + key.setSecretKey(secretKey); - @Override - public List getKeyMetadata() { - if (kid != null && secretKey != null) { - SecretKeyMetadata k = new SecretKeyMetadata(); - k.setProviderId(model.getId()); - k.setProviderPriority(model.get(Attributes.PRIORITY_KEY, 0l)); - k.setKid(kid); - if (isActive()) { - k.setStatus(KeyMetadata.Status.ACTIVE); - } else if (isEnabled()) { - k.setStatus(KeyMetadata.Status.PASSIVE); - } else { - k.setStatus(KeyMetadata.Status.DISABLED); - } - return Collections.singletonList(k); - } else { - return Collections.emptyList(); - } + return Collections.singletonList(key); } @Override public void close() { } - private boolean isEnabled() { - return secretKey != null && enabled; - } - - private boolean isActive() { - return isEnabled() && active; - } } diff --git a/services/src/main/java/org/keycloak/keys/GeneratedSecretKeyProviderFactory.java b/services/src/main/java/org/keycloak/keys/GeneratedSecretKeyProviderFactory.java index 3211cfc62ecb..c6c5d41622c4 100644 --- a/services/src/main/java/org/keycloak/keys/GeneratedSecretKeyProviderFactory.java +++ b/services/src/main/java/org/keycloak/keys/GeneratedSecretKeyProviderFactory.java @@ -66,17 +66,5 @@ private void generateSecret(ComponentModel model, int size) { protected abstract Logger logger(); - @Override - public void init(Config.Scope config) { - } - - @Override - public void postInit(KeycloakSessionFactory factory) { - } - - @Override - public void close() { - } - protected abstract int getDefaultKeySize(); } diff --git a/services/src/main/java/org/keycloak/keys/ImportedRsaKeyProvider.java b/services/src/main/java/org/keycloak/keys/ImportedRsaKeyProvider.java index 6fd7a920d8e9..67968a48f0a3 100644 --- a/services/src/main/java/org/keycloak/keys/ImportedRsaKeyProvider.java +++ b/services/src/main/java/org/keycloak/keys/ImportedRsaKeyProvider.java @@ -20,6 +20,7 @@ import org.keycloak.common.util.KeyUtils; import org.keycloak.common.util.PemUtils; import org.keycloak.component.ComponentModel; +import org.keycloak.crypto.KeyWrapper; import org.keycloak.models.RealmModel; import java.security.KeyPair; @@ -38,7 +39,7 @@ public ImportedRsaKeyProvider(RealmModel realm, ComponentModel model) { } @Override - public Keys loadKeys(RealmModel realm, ComponentModel model) { + public KeyWrapper loadKey(RealmModel realm, ComponentModel model) { String privateRsaKeyPem = model.getConfig().getFirst(Attributes.PRIVATE_KEY_KEY); String certificatePem = model.getConfig().getFirst(Attributes.CERTIFICATE_KEY); @@ -48,9 +49,7 @@ public Keys loadKeys(RealmModel realm, ComponentModel model) { KeyPair keyPair = new KeyPair(publicKey, privateKey); X509Certificate certificate = PemUtils.decodeCertificate(certificatePem); - String kid = KeyUtils.createKeyId(keyPair.getPublic()); - - return new Keys(kid, keyPair, certificate); + return createKeyWrapper(keyPair, certificate); } } diff --git a/services/src/main/java/org/keycloak/keys/ImportedRsaKeyProviderFactory.java b/services/src/main/java/org/keycloak/keys/ImportedRsaKeyProviderFactory.java index 5dbe4bc15242..d46a77a341a6 100644 --- a/services/src/main/java/org/keycloak/keys/ImportedRsaKeyProviderFactory.java +++ b/services/src/main/java/org/keycloak/keys/ImportedRsaKeyProviderFactory.java @@ -17,14 +17,12 @@ package org.keycloak.keys; -import org.keycloak.Config; import org.keycloak.common.util.CertificateUtils; import org.keycloak.common.util.KeyUtils; import org.keycloak.common.util.PemUtils; import org.keycloak.component.ComponentModel; import org.keycloak.component.ComponentValidationException; import org.keycloak.models.KeycloakSession; -import org.keycloak.models.KeycloakSessionFactory; import org.keycloak.models.RealmModel; import org.keycloak.provider.ConfigurationValidationHelper; import org.keycloak.provider.ProviderConfigProperty; @@ -106,18 +104,6 @@ public List getConfigProperties() { return CONFIG_PROPERTIES; } - @Override - public void init(Config.Scope config) { - } - - @Override - public void postInit(KeycloakSessionFactory factory) { - } - - @Override - public void close() { - } - @Override public String getId() { return ID; diff --git a/services/src/main/java/org/keycloak/keys/JavaKeystoreKeyProvider.java b/services/src/main/java/org/keycloak/keys/JavaKeystoreKeyProvider.java index 8c98bb4556b9..ba5dfc6713ba 100644 --- a/services/src/main/java/org/keycloak/keys/JavaKeystoreKeyProvider.java +++ b/services/src/main/java/org/keycloak/keys/JavaKeystoreKeyProvider.java @@ -17,23 +17,16 @@ package org.keycloak.keys; -import org.jboss.logging.Logger; import org.keycloak.common.util.CertificateUtils; import org.keycloak.common.util.KeyUtils; import org.keycloak.component.ComponentModel; +import org.keycloak.crypto.KeyWrapper; import org.keycloak.models.RealmModel; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; -import java.security.KeyPair; -import java.security.KeyStore; -import java.security.KeyStoreException; -import java.security.NoSuchAlgorithmException; -import java.security.PrivateKey; -import java.security.PublicKey; -import java.security.UnrecoverableKeyException; -import java.security.cert.Certificate; +import java.security.*; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; @@ -42,14 +35,12 @@ */ public class JavaKeystoreKeyProvider extends AbstractRsaKeyProvider { - private static final Logger logger = Logger.getLogger(JavaKeystoreKeyProvider.class); - public JavaKeystoreKeyProvider(RealmModel realm, ComponentModel model) { super(realm, model); } @Override - protected Keys loadKeys(RealmModel realm, ComponentModel model) { + protected KeyWrapper loadKey(RealmModel realm, ComponentModel model) { try { KeyStore keyStore = KeyStore.getInstance("JKS"); keyStore.load(new FileInputStream(model.get(JavaKeystoreKeyProviderFactory.KEYSTORE_KEY)), model.get(JavaKeystoreKeyProviderFactory.KEYSTORE_PASSWORD_KEY).toCharArray()); @@ -64,9 +55,7 @@ protected Keys loadKeys(RealmModel realm, ComponentModel model) { certificate = CertificateUtils.generateV1SelfSignedCertificate(keyPair, realm.getName()); } - String kid = KeyUtils.createKeyId(keyPair.getPublic()); - - return new Keys(kid, keyPair, certificate); + return createKeyWrapper(keyPair, certificate); } catch (KeyStoreException kse) { throw new RuntimeException("KeyStore error on server. " + kse.getMessage(), kse); } catch (FileNotFoundException fnfe) { diff --git a/services/src/main/java/org/keycloak/keys/JavaKeystoreKeyProviderFactory.java b/services/src/main/java/org/keycloak/keys/JavaKeystoreKeyProviderFactory.java index 51b726a57b3c..325be9e7ab3b 100644 --- a/services/src/main/java/org/keycloak/keys/JavaKeystoreKeyProviderFactory.java +++ b/services/src/main/java/org/keycloak/keys/JavaKeystoreKeyProviderFactory.java @@ -77,7 +77,7 @@ public void validateConfiguration(KeycloakSession session, RealmModel realm, Com try { new JavaKeystoreKeyProvider(session.getContext().getRealm(), model) - .loadKeys(session.getContext().getRealm(), model); + .loadKey(session.getContext().getRealm(), model); } catch (Throwable t) { logger.error("Failed to load keys.", t); throw new ComponentValidationException("Failed to load keys. " + t.getMessage(), t); @@ -94,18 +94,6 @@ public List getConfigProperties() { return CONFIG_PROPERTIES; } - @Override - public void init(Config.Scope config) { - } - - @Override - public void postInit(KeycloakSessionFactory factory) { - } - - @Override - public void close() { - } - @Override public String getId() { return ID; diff --git a/services/src/main/java/org/keycloak/protocol/oidc/OIDCLoginProtocolService.java b/services/src/main/java/org/keycloak/protocol/oidc/OIDCLoginProtocolService.java index 6fa6705c858d..c1952a55cc67 100644 --- a/services/src/main/java/org/keycloak/protocol/oidc/OIDCLoginProtocolService.java +++ b/services/src/main/java/org/keycloak/protocol/oidc/OIDCLoginProtocolService.java @@ -26,7 +26,6 @@ import org.keycloak.jose.jwk.JSONWebKeySet; import org.keycloak.jose.jwk.JWK; import org.keycloak.jose.jwk.JWKBuilder; -import org.keycloak.keys.KeyMetadata; import org.keycloak.keys.RsaKeyMetadata; import org.keycloak.models.Constants; import org.keycloak.models.KeycloakSession; @@ -195,7 +194,7 @@ public Response getVersionPreflight() { @Produces(MediaType.APPLICATION_JSON) @NoCache public Response certs() { - List publicKeys = session.keys().getRsaKeys(realm, false); + List publicKeys = session.keys().getRsaKeys(realm); JWK[] keys = new JWK[publicKeys.size()]; int i = 0; diff --git a/services/src/main/java/org/keycloak/protocol/saml/SamlService.java b/services/src/main/java/org/keycloak/protocol/saml/SamlService.java index e6f1833b4489..b98727c52cc0 100755 --- a/services/src/main/java/org/keycloak/protocol/saml/SamlService.java +++ b/services/src/main/java/org/keycloak/protocol/saml/SamlService.java @@ -23,6 +23,7 @@ import org.keycloak.common.VerificationException; import org.keycloak.common.util.PemUtils; import org.keycloak.common.util.StreamUtil; +import org.keycloak.crypto.KeyStatus; import org.keycloak.dom.saml.v2.SAML2Object; import org.keycloak.dom.saml.v2.assertion.BaseIDAbstractType; import org.keycloak.dom.saml.v2.assertion.NameIDType; @@ -80,7 +81,6 @@ import java.util.TreeSet; import org.keycloak.common.util.StringPropertyReplacer; import org.keycloak.dom.saml.v2.metadata.KeyTypes; -import org.keycloak.keys.KeyMetadata; import org.keycloak.rotation.HardcodedKeyLocator; import org.keycloak.rotation.KeyLocator; import org.keycloak.saml.SPMetadataDescriptor; @@ -591,8 +591,8 @@ public static String getIDPMetadataDescriptor(UriInfo uriInfo, KeycloakSession s StringBuilder keysString = new StringBuilder(); Set keys = new TreeSet<>((o1, o2) -> o1.getStatus() == o2.getStatus() // Status can be only PASSIVE OR ACTIVE, push PASSIVE to end of list ? (int) (o2.getProviderPriority() - o1.getProviderPriority()) - : (o1.getStatus() == KeyMetadata.Status.PASSIVE ? 1 : -1)); - keys.addAll(session.keys().getRsaKeys(realm, false)); + : (o1.getStatus() == KeyStatus.PASSIVE ? 1 : -1)); + keys.addAll(session.keys().getRsaKeys(realm)); for (RsaKeyMetadata key : keys) { addKeyInfo(keysString, key, KeyTypes.SIGNING.value()); } diff --git a/services/src/main/java/org/keycloak/protocol/saml/installation/SamlIDPDescriptorClientInstallation.java b/services/src/main/java/org/keycloak/protocol/saml/installation/SamlIDPDescriptorClientInstallation.java index 2261d520e632..b3b6d6ade78d 100755 --- a/services/src/main/java/org/keycloak/protocol/saml/installation/SamlIDPDescriptorClientInstallation.java +++ b/services/src/main/java/org/keycloak/protocol/saml/installation/SamlIDPDescriptorClientInstallation.java @@ -19,6 +19,8 @@ import org.keycloak.Config; import org.keycloak.common.util.PemUtils; +import org.keycloak.crypto.KeyStatus; +import org.keycloak.dom.saml.v2.metadata.KeyTypes; import org.keycloak.keys.RsaKeyMetadata; import org.keycloak.models.ClientModel; import org.keycloak.models.KeycloakSession; @@ -27,6 +29,7 @@ import org.keycloak.protocol.ClientInstallationProvider; import org.keycloak.protocol.saml.SamlClient; import org.keycloak.protocol.saml.SamlProtocol; +import org.keycloak.saml.SPMetadataDescriptor; import org.keycloak.services.resources.RealmsResource; import javax.ws.rs.core.MediaType; @@ -35,9 +38,6 @@ import java.net.URI; import java.util.Set; import java.util.TreeSet; -import org.keycloak.dom.saml.v2.metadata.KeyTypes; -import org.keycloak.keys.KeyMetadata; -import org.keycloak.saml.SPMetadataDescriptor; /** * @author Bill Burke @@ -90,8 +90,8 @@ public static String getIDPDescriptorForClient(KeycloakSession session, RealmMod // keys Set keys = new TreeSet<>((o1, o2) -> o1.getStatus() == o2.getStatus() // Status can be only PASSIVE OR ACTIVE, push PASSIVE to end of list ? (int) (o2.getProviderPriority() - o1.getProviderPriority()) - : (o1.getStatus() == KeyMetadata.Status.PASSIVE ? 1 : -1)); - keys.addAll(session.keys().getRsaKeys(realm, false)); + : (o1.getStatus() == KeyStatus.PASSIVE ? 1 : -1)); + keys.addAll(session.keys().getRsaKeys(realm)); for (RsaKeyMetadata key : keys) { addKeyInfo(sb, key, KeyTypes.SIGNING.value()); } diff --git a/services/src/main/java/org/keycloak/services/resources/admin/KeyResource.java b/services/src/main/java/org/keycloak/services/resources/admin/KeyResource.java index 87bb486d4b06..da0f3cbea09e 100644 --- a/services/src/main/java/org/keycloak/services/resources/admin/KeyResource.java +++ b/services/src/main/java/org/keycloak/services/resources/admin/KeyResource.java @@ -19,9 +19,9 @@ import org.jboss.resteasy.annotations.cache.NoCache; import org.keycloak.common.util.PemUtils; +import org.keycloak.crypto.KeyWrapper; import org.keycloak.jose.jws.AlgorithmType; import org.keycloak.keys.SecretKeyMetadata; -import org.keycloak.keys.RsaKeyMetadata; import org.keycloak.models.KeycloakSession; import org.keycloak.models.KeyManager; import org.keycloak.models.RealmModel; @@ -58,48 +58,30 @@ public KeyResource(RealmModel realm, KeycloakSession session, AdminPermissionEva public KeysMetadataRepresentation getKeyMetadata() { auth.realm().requireViewRealm(); - KeyManager keystore = session.keys(); - KeysMetadataRepresentation keys = new KeysMetadataRepresentation(); + keys.setKeys(new LinkedList<>()); + keys.setActive(new HashMap<>()); - Map active = new HashMap<>(); - active.put(AlgorithmType.RSA.name(), keystore.getActiveRsaKey(realm).getKid()); - active.put(AlgorithmType.HMAC.name(), keystore.getActiveHmacKey(realm).getKid()); - active.put(AlgorithmType.AES.name(), keystore.getActiveAesKey(realm).getKid()); - keys.setActive(active); - - List l = new LinkedList<>(); - for (RsaKeyMetadata m : session.keys().getRsaKeys(realm, true)) { - KeysMetadataRepresentation.KeyMetadataRepresentation r = new KeysMetadataRepresentation.KeyMetadataRepresentation(); - r.setProviderId(m.getProviderId()); - r.setProviderPriority(m.getProviderPriority()); - r.setKid(m.getKid()); - r.setStatus(m.getStatus() != null ? m.getStatus().name() : null); - r.setType(AlgorithmType.RSA.name()); - r.setPublicKey(PemUtils.encodeKey(m.getPublicKey())); - r.setCertificate(PemUtils.encodeCertificate(m.getCertificate())); - l.add(r); - } - for (SecretKeyMetadata m : session.keys().getHmacKeys(realm, true)) { + for (KeyWrapper key : session.keys().getKeys(realm)) { KeysMetadataRepresentation.KeyMetadataRepresentation r = new KeysMetadataRepresentation.KeyMetadataRepresentation(); - r.setProviderId(m.getProviderId()); - r.setProviderPriority(m.getProviderPriority()); - r.setKid(m.getKid()); - r.setStatus(m.getStatus() != null ? m.getStatus().name() : null); - r.setType(AlgorithmType.HMAC.name()); - l.add(r); - } - for (SecretKeyMetadata m : session.keys().getAesKeys(realm, true)) { - KeysMetadataRepresentation.KeyMetadataRepresentation r = new KeysMetadataRepresentation.KeyMetadataRepresentation(); - r.setProviderId(m.getProviderId()); - r.setProviderPriority(m.getProviderPriority()); - r.setKid(m.getKid()); - r.setStatus(m.getStatus() != null ? m.getStatus().name() : null); - r.setType(AlgorithmType.AES.name()); - l.add(r); - } + r.setProviderId(key.getProviderId()); + r.setProviderPriority(key.getProviderPriority()); + r.setKid(key.getKid()); + r.setStatus(key.getStatus() != null ? key.getStatus().name() : null); + r.setType(key.getType()); + r.setAlgorithms(key.getAlgorithms()); + r.setPublicKey(key.getVerifyKey() != null ? PemUtils.encodeKey(key.getVerifyKey()) : null); + r.setCertificate(key.getCertificate() != null ? PemUtils.encodeCertificate(key.getCertificate()) : null); + keys.getKeys().add(r); - keys.setKeys(l); + if (key.getStatus().isActive()) { + for (String a : key.getAlgorithms()) { + if (!keys.getActive().containsKey(a)) { + keys.getActive().put(a, key.getKid()); + } + } + } + } return keys; } diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/admin/ApiUtil.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/admin/ApiUtil.java index 208ae9145fbf..7bff5ac1ce0e 100644 --- a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/admin/ApiUtil.java +++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/admin/ApiUtil.java @@ -23,6 +23,7 @@ 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.representations.idm.ClientRepresentation; import org.keycloak.representations.idm.ClientScopeRepresentation; import org.keycloak.representations.idm.CredentialRepresentation; @@ -255,7 +256,7 @@ public static AuthorizationResource findAuthorizationSettings(RealmResource real public static KeysMetadataRepresentation.KeyMetadataRepresentation findActiveKey(RealmResource realm) { KeysMetadataRepresentation keyMetadata = realm.keys().getKeyMetadata(); - String activeKid = keyMetadata.getActive().get("RSA"); + String activeKid = keyMetadata.getActive().get(Algorithm.RS256); for (KeysMetadataRepresentation.KeyMetadataRepresentation rep : keyMetadata.getKeys()) { if (rep.getKid().equals(activeKid)) { return rep; diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/KeyUtils.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/KeyUtils.java index afe2f51b20e8..28627554e150 100644 --- a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/KeyUtils.java +++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/KeyUtils.java @@ -1,6 +1,7 @@ package org.keycloak.testsuite.util; import org.keycloak.common.util.BouncyIntegration; +import org.keycloak.representations.idm.KeysMetadataRepresentation; import java.security.KeyFactory; import java.security.NoSuchAlgorithmException; @@ -39,4 +40,15 @@ public static PrivateKey privateKeyFromString(String key) { throw new RuntimeException(e); } } + + public static KeysMetadataRepresentation.KeyMetadataRepresentation getActiveKey(KeysMetadataRepresentation keys, String algorithm) { + String kid = keys.getActive().get(algorithm); + for (KeysMetadataRepresentation.KeyMetadataRepresentation k : keys.getKeys()) { + if (k.getKid().equals(kid)) { + return k; + } + } + throw new RuntimeException("Active key not found"); + } + } diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/OAuthClient.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/OAuthClient.java index 9e0f12dd9588..21186bda8c06 100644 --- a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/OAuthClient.java +++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/OAuthClient.java @@ -38,6 +38,7 @@ import org.keycloak.common.util.KeystoreUtil; import org.keycloak.common.util.PemUtils; import org.keycloak.constants.AdapterConstants; +import org.keycloak.crypto.Algorithm; import org.keycloak.jose.jwk.JSONWebKeySet; import org.keycloak.jose.jws.JWSInput; import org.keycloak.jose.jws.JWSInputException; @@ -1099,7 +1100,7 @@ public Map getHeaders() { public PublicKey getRealmPublicKey(String realm) { if (!publicKeys.containsKey(realm)) { KeysMetadataRepresentation keyMetadata = adminClient.realms().realm(realm).keys().getKeyMetadata(); - String activeKid = keyMetadata.getActive().get("RSA"); + String activeKid = keyMetadata.getActive().get(Algorithm.RS256); PublicKey publicKey = null; for (KeysMetadataRepresentation.KeyMetadataRepresentation rep : keyMetadata.getKeys()) { if (rep.getKid().equals(activeKid)) { diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/servlet/OIDCPublicKeyRotationAdapterTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/servlet/OIDCPublicKeyRotationAdapterTest.java index a1cb30936623..bd10c3a821ca 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/servlet/OIDCPublicKeyRotationAdapterTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/servlet/OIDCPublicKeyRotationAdapterTest.java @@ -42,7 +42,7 @@ import org.keycloak.common.util.StreamUtil; import org.keycloak.common.util.Time; import org.keycloak.constants.AdapterConstants; -import org.keycloak.jose.jws.AlgorithmType; +import org.keycloak.crypto.Algorithm; import org.keycloak.keys.KeyProvider; import org.keycloak.protocol.oidc.OIDCAdvancedConfigWrapper; import org.keycloak.protocol.oidc.OIDCLoginProtocolService; @@ -308,7 +308,7 @@ private void generateNewRealmKey() { private String getActiveKeyProvider() { KeysMetadataRepresentation keyMetadata = adminClient.realm(DEMO).keys().getKeyMetadata(); - String activeKid = keyMetadata.getActive().get(AlgorithmType.RSA.name()); + String activeKid = keyMetadata.getActive().get(Algorithm.RS256); for (KeysMetadataRepresentation.KeyMetadataRepresentation rep : keyMetadata.getKeys()) { if (rep.getKid().equals(activeKid)) { return rep.getProviderId(); diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOIDCBrokerWithSignatureTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOIDCBrokerWithSignatureTest.java index 83496a378e76..f98a5fc804ec 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOIDCBrokerWithSignatureTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOIDCBrokerWithSignatureTest.java @@ -28,6 +28,7 @@ import org.keycloak.admin.client.resource.RealmResource; import org.keycloak.common.util.*; import org.keycloak.connections.infinispan.InfinispanConnectionProvider; +import org.keycloak.crypto.Algorithm; import org.keycloak.keys.KeyProvider; import org.keycloak.keys.PublicKeyStorageUtils; import org.keycloak.protocol.oidc.OIDCLoginProtocolService; @@ -293,7 +294,7 @@ public void testPublicKeyCacheInvalidatedWhenProviderUpdated() throws Exception private void rotateKeys() { - String activeKid = providerRealm().keys().getKeyMetadata().getActive().get("RSA"); + String activeKid = providerRealm().keys().getKeyMetadata().getActive().get(Algorithm.RS256); // Rotate public keys on the parent broker String realmId = providerRealm().toRepresentation().getId(); @@ -308,7 +309,7 @@ private void rotateKeys() { assertEquals(201, response.getStatus()); response.close(); - String updatedActiveKid = providerRealm().keys().getKeyMetadata().getActive().get("RSA"); + String updatedActiveKid = providerRealm().keys().getKeyMetadata().getActive().get(Algorithm.RS256); assertNotEquals(activeKid, updatedActiveKid); } diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcSamlSignedBrokerTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcSamlSignedBrokerTest.java index e447998b867e..6887f74db959 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcSamlSignedBrokerTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcSamlSignedBrokerTest.java @@ -2,10 +2,12 @@ import org.keycloak.admin.client.resource.ClientResource; import org.keycloak.broker.saml.SAMLIdentityProviderConfig; +import org.keycloak.crypto.Algorithm; import org.keycloak.dom.saml.v2.protocol.AuthnRequestType; import org.keycloak.protocol.saml.SamlConfigAttributes; import org.keycloak.representations.idm.ClientRepresentation; import org.keycloak.representations.idm.IdentityProviderRepresentation; +import org.keycloak.representations.idm.KeysMetadataRepresentation; import org.keycloak.representations.idm.RealmRepresentation; import org.keycloak.saml.common.constants.JBossSAMLURIConstants; import org.keycloak.saml.common.util.DocumentUtil; @@ -16,6 +18,7 @@ import org.keycloak.testsuite.saml.AbstractSamlTest; import org.keycloak.testsuite.updaters.ClientAttributeUpdater; import org.keycloak.testsuite.updaters.IdentityProviderAttributeUpdater; +import org.keycloak.testsuite.util.KeyUtils; import org.keycloak.testsuite.util.SamlClient; import org.keycloak.testsuite.util.SamlClient.Binding; import org.keycloak.testsuite.util.SamlClientBuilder; @@ -66,7 +69,7 @@ public RealmRepresentation createConsumerRealm() { public List createProviderClients(SuiteContext suiteContext) { List clientRepresentationList = super.createProviderClients(suiteContext); - String consumerCert = adminClient.realm(consumerRealmName()).keys().getKeyMetadata().getKeys().get(0).getCertificate(); + String consumerCert = KeyUtils.getActiveKey(adminClient.realm(consumerRealmName()).keys().getKeyMetadata(), Algorithm.RS256).getCertificate(); Assert.assertThat(consumerCert, Matchers.notNullValue()); for (ClientRepresentation client : clientRepresentationList) { @@ -93,7 +96,7 @@ public List createProviderClients(SuiteContext suiteContex public IdentityProviderRepresentation setUpIdentityProvider(SuiteContext suiteContext) { IdentityProviderRepresentation result = super.setUpIdentityProvider(suiteContext); - String providerCert = adminClient.realm(providerRealmName()).keys().getKeyMetadata().getKeys().get(0).getCertificate(); + String providerCert = KeyUtils.getActiveKey(adminClient.realm(providerRealmName()).keys().getKeyMetadata(), Algorithm.RS256).getCertificate(); Assert.assertThat(providerCert, Matchers.notNullValue()); Map config = result.getConfig(); @@ -121,10 +124,10 @@ public void withSignedEncryptedAssertions(Runnable testBody, boolean signedAsser final ClientResource clientResource = realmsResouce().realm(bc.providerRealmName()).clients().get(client.getId()); Assert.assertThat(clientResource, Matchers.notNullValue()); - String providerCert = adminClient.realm(bc.providerRealmName()).keys().getKeyMetadata().getKeys().get(0).getCertificate(); + String providerCert = KeyUtils.getActiveKey(adminClient.realm(bc.providerRealmName()).keys().getKeyMetadata(), Algorithm.RS256).getCertificate(); Assert.assertThat(providerCert, Matchers.notNullValue()); - String consumerCert = adminClient.realm(bc.consumerRealmName()).keys().getKeyMetadata().getKeys().get(0).getCertificate(); + String consumerCert = KeyUtils.getActiveKey(adminClient.realm(bc.consumerRealmName()).keys().getKeyMetadata(), Algorithm.RS256).getCertificate(); Assert.assertThat(consumerCert, Matchers.notNullValue()); try (Closeable idpUpdater = new IdentityProviderAttributeUpdater(identityProviderResource) diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/keys/GeneratedHmacKeyProviderTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/keys/GeneratedHmacKeyProviderTest.java index efddeeab7046..4734ffdea9fb 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/keys/GeneratedHmacKeyProviderTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/keys/GeneratedHmacKeyProviderTest.java @@ -24,6 +24,8 @@ import org.junit.Test; import org.keycloak.common.util.Base64Url; import org.keycloak.common.util.MultivaluedHashMap; +import org.keycloak.crypto.Algorithm; +import org.keycloak.crypto.KeyType; import org.keycloak.jose.jws.AlgorithmType; import org.keycloak.keys.GeneratedHmacKeyProviderFactory; import org.keycloak.keys.KeyProvider; @@ -90,14 +92,14 @@ public void defaultKeysize() throws Exception { KeysMetadataRepresentation.KeyMetadataRepresentation key = null; for (KeysMetadataRepresentation.KeyMetadataRepresentation k : keys.getKeys()) { - if (k.getType().equals(AlgorithmType.HMAC.name())) { + if (k.getAlgorithms().contains(Algorithm.HS256)) { key = k; break; } } assertEquals(id, key.getProviderId()); - assertEquals(AlgorithmType.HMAC.name(), key.getType()); + assertEquals(KeyType.OCT, key.getType()); assertEquals(priority, key.getProviderPriority()); ComponentRepresentation component = testingClient.server("test").fetch(RunHelpers.internalComponent(id)); @@ -125,14 +127,14 @@ public void largeKeysize() throws Exception { KeysMetadataRepresentation.KeyMetadataRepresentation key = null; for (KeysMetadataRepresentation.KeyMetadataRepresentation k : keys.getKeys()) { - if (k.getType().equals(AlgorithmType.HMAC.name())) { + if (k.getAlgorithms().contains(Algorithm.HS256)) { key = k; break; } } assertEquals(id, key.getProviderId()); - assertEquals(AlgorithmType.HMAC.name(), key.getType()); + assertEquals(KeyType.OCT, key.getType()); assertEquals(priority, key.getProviderPriority()); ComponentRepresentation component = testingClient.server("test").fetch(RunHelpers.internalComponent(id)); diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/keys/ImportedRsaKeyProviderTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/keys/ImportedRsaKeyProviderTest.java index e8d11ecb374c..47e850e9280a 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/keys/ImportedRsaKeyProviderTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/keys/ImportedRsaKeyProviderTest.java @@ -24,6 +24,7 @@ import org.keycloak.common.util.KeyUtils; import org.keycloak.common.util.MultivaluedHashMap; import org.keycloak.common.util.PemUtils; +import org.keycloak.crypto.Algorithm; import org.keycloak.jose.jws.AlgorithmType; import org.keycloak.keys.Attributes; import org.keycloak.keys.ImportedRsaKeyProviderFactory; @@ -90,7 +91,7 @@ public void privateKeyOnly() throws Exception { KeysMetadataRepresentation keys = adminClient.realm("test").keys().getKeyMetadata(); - assertEquals(kid, keys.getActive().get(AlgorithmType.RSA.name())); + assertEquals(kid, keys.getActive().get(Algorithm.RS256)); KeysMetadataRepresentation.KeyMetadataRepresentation key = keys.getKeys().get(0); diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/keys/KeyRotationTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/keys/KeyRotationTest.java index 9700a296abe1..cbc8db5752f9 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/keys/KeyRotationTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/keys/KeyRotationTest.java @@ -30,6 +30,7 @@ import org.keycloak.common.util.KeyUtils; import org.keycloak.common.util.MultivaluedHashMap; import org.keycloak.common.util.PemUtils; +import org.keycloak.crypto.Algorithm; import org.keycloak.keys.Attributes; import org.keycloak.keys.GeneratedHmacKeyProviderFactory; import org.keycloak.keys.KeyProvider; @@ -218,7 +219,7 @@ public void providerOrder() throws Exception { PublicKey keys2 = createKeys2(); KeysMetadataRepresentation keyMetadata = adminClient.realm("test").keys().getKeyMetadata(); - assertEquals(PemUtils.encodeKey(keys2), keyMetadata.getKeys().get(0).getPublicKey()); + assertEquals(PemUtils.encodeKey(keys2), org.keycloak.testsuite.util.KeyUtils.getActiveKey(keyMetadata, Algorithm.RS256).getPublicKey()); dropKeys1(); dropKeys2(); @@ -227,7 +228,7 @@ public void providerOrder() throws Exception { @Test public void rotateKeys() throws InterruptedException { for (int i = 0; i < 10; i++) { - String activeKid = adminClient.realm("test").keys().getKeyMetadata().getActive().get("RSA"); + String activeKid = adminClient.realm("test").keys().getKeyMetadata().getActive().get(Algorithm.RS256); // Rotate public keys on the parent broker String realmId = adminClient.realm("test").toRepresentation().getId(); @@ -244,7 +245,7 @@ public void rotateKeys() throws InterruptedException { getCleanup().addComponentId(newId); response.close(); - String updatedActiveKid = adminClient.realm("test").keys().getKeyMetadata().getActive().get("RSA"); + String updatedActiveKid = adminClient.realm("test").keys().getKeyMetadata().getActive().get(Algorithm.RS256); assertNotEquals(activeKid, updatedActiveKid); } } diff --git a/testsuite/integration-deprecated/src/test/java/org/keycloak/testsuite/ApiUtil.java b/testsuite/integration-deprecated/src/test/java/org/keycloak/testsuite/ApiUtil.java index b2fdf8f4d397..2ab996de94cb 100644 --- a/testsuite/integration-deprecated/src/test/java/org/keycloak/testsuite/ApiUtil.java +++ b/testsuite/integration-deprecated/src/test/java/org/keycloak/testsuite/ApiUtil.java @@ -16,8 +16,6 @@ */ package org.keycloak.testsuite; -import org.jboss.logging.Logger; - import javax.ws.rs.WebApplicationException; import javax.ws.rs.core.Response; import javax.ws.rs.core.Response.Status; @@ -29,8 +27,6 @@ */ public class ApiUtil { - private static final Logger log = Logger.getLogger(ApiUtil.class); - public static String getCreatedId(Response response) { URI location = response.getLocation(); if (!response.getStatusInfo().equals(Status.CREATED)) { diff --git a/themes/src/main/resources/theme/base/admin/messages/admin-messages_en.properties b/themes/src/main/resources/theme/base/admin/messages/admin-messages_en.properties index c13aecdc230b..a062db29e0ac 100644 --- a/themes/src/main/resources/theme/base/admin/messages/admin-messages_en.properties +++ b/themes/src/main/resources/theme/base/admin/messages/admin-messages_en.properties @@ -1388,7 +1388,6 @@ authz-evaluation-authorization-data.tooltip=Represents a token carrying authoriz authz-show-authorization-data=Show Authorization Data keys=Keys -all=All status=Status keystore=Keystore keystores=Keystores @@ -1396,6 +1395,10 @@ add-keystore=Add Keystore add-keystore.placeholder=Add keystore... view=View active=Active +passive=Passive +disabled=Disabled +algorithms=Algorithms +providerHelpText=Provider description Sunday=Sunday Monday=Monday diff --git a/themes/src/main/resources/theme/base/admin/resources/js/app.js b/themes/src/main/resources/theme/base/admin/resources/js/app.js index 6aaff543941a..f03208c59ce1 100755 --- a/themes/src/main/resources/theme/base/admin/resources/js/app.js +++ b/themes/src/main/resources/theme/base/admin/resources/js/app.js @@ -289,8 +289,8 @@ module.config([ '$routeProvider', function($routeProvider) { }, controller : 'RealmKeysCtrl' }) - .when('/realms/:realm/keys/list', { - templateUrl : resourceUrl + '/partials/realm-keys-list.html', + .when('/realms/:realm/keys/passive', { + templateUrl : resourceUrl + '/partials/realm-keys-passive.html', resolve : { realm : function(RealmLoader) { return RealmLoader(); @@ -304,6 +304,21 @@ module.config([ '$routeProvider', function($routeProvider) { }, controller : 'RealmKeysCtrl' }) + .when('/realms/:realm/keys/disabled', { + templateUrl : resourceUrl + '/partials/realm-keys-disabled.html', + resolve : { + realm : function(RealmLoader) { + return RealmLoader(); + }, + serverInfo : function(ServerInfoLoader) { + return ServerInfoLoader(); + }, + keys: function(RealmKeysLoader) { + return RealmKeysLoader(); + } + }, + controller : 'RealmKeysCtrl' + }) .when('/realms/:realm/keys/providers', { templateUrl : resourceUrl + '/partials/realm-keys-providers.html', resolve : { diff --git a/themes/src/main/resources/theme/base/admin/resources/partials/realm-keys-disabled.html b/themes/src/main/resources/theme/base/admin/resources/partials/realm-keys-disabled.html new file mode 100755 index 000000000000..997c5137e925 --- /dev/null +++ b/themes/src/main/resources/theme/base/admin/resources/partials/realm-keys-disabled.html @@ -0,0 +1,71 @@ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +
+ +
+
+
+
+
{{:: 'algorithms' | translate}}{{:: 'type' | translate}}{{:: 'kid' | translate}}{{:: 'priority' | translate}}{{:: 'provider' | translate}}{{:: 'publicKeys' | translate}}
{{key.algorithm.sort().join(', ')}}{{key.type}}{{key.kid}}{{key.providerPriority}}{{key.provider.name}}{{:: 'publicKey' | translate}}{{:: 'certificate' | translate}}
+ +
+ + \ No newline at end of file diff --git a/themes/src/main/resources/theme/base/admin/resources/partials/realm-keys-list.html b/themes/src/main/resources/theme/base/admin/resources/partials/realm-keys-passive.html similarity index 68% rename from themes/src/main/resources/theme/base/admin/resources/partials/realm-keys-list.html rename to themes/src/main/resources/theme/base/admin/resources/partials/realm-keys-passive.html index 41f1684e24ca..461dcc1fda11 100755 --- a/themes/src/main/resources/theme/base/admin/resources/partials/realm-keys-list.html +++ b/themes/src/main/resources/theme/base/admin/resources/partials/realm-keys-passive.html @@ -20,15 +20,30 @@ + + + + - @@ -36,9 +51,9 @@ - + + - diff --git a/themes/src/main/resources/theme/base/admin/resources/partials/realm-keys-providers.html b/themes/src/main/resources/theme/base/admin/resources/partials/realm-keys-providers.html index fceea49b791b..c303a38bc298 100755 --- a/themes/src/main/resources/theme/base/admin/resources/partials/realm-keys-providers.html +++ b/themes/src/main/resources/theme/base/admin/resources/partials/realm-keys-providers.html @@ -20,16 +20,26 @@
+
+
+
+ +
+ +
+
+
+
+
{{:: 'algorithms' | translate}} {{:: 'type' | translate}}{{:: 'status' | translate}} {{:: 'kid' | translate}} {{:: 'priority' | translate}} {{:: 'provider' | translate}}
{{key.algorithm.sort().join(', ')}} {{key.type}}{{key.status}} {{key.kid}} {{key.providerPriority}} {{key.provider.name}}
- - + - - + - - - - + + + diff --git a/themes/src/main/resources/theme/base/admin/resources/partials/realm-keys.html b/themes/src/main/resources/theme/base/admin/resources/partials/realm-keys.html index 81215d063c5a..2bc75a8a5cce 100755 --- a/themes/src/main/resources/theme/base/admin/resources/partials/realm-keys.html +++ b/themes/src/main/resources/theme/base/admin/resources/partials/realm-keys.html @@ -1,25 +1,61 @@ + +
-
-
+
+
+
+
+ +
+ +
+
+
+ +
{{:: 'type' | translate}} {{:: 'name' | translate}}{{:: 'id' | translate}} {{:: 'provider' | translate}}{{:: 'providerHelpText' | translate}} {{:: 'priority' | translate}} {{:: 'actions' | translate}}
{{instance.provider.metadata.algorithmType}}{{instance.name}}{{instance.id}}
{{instance.name}} {{instance.providerId}}{{instance.provider.helpText}} {{instance.config['priority'][0]}} {{:: 'edit' | translate}} {{:: 'delete' | translate}}
+ + + + + - + + +
+
+
+
+ +
+ +
+
+
+
+
{{:: 'algorithms' | translate}} {{:: 'type' | translate}} {{:: 'kid' | translate}}{{:: 'priority' | translate}} {{:: 'provider' | translate}} {{:: 'publicKeys' | translate}}
{{key.algorithm.sort().join(', ')}} {{key.type}} {{key.kid}}{{key.providerPriority}} {{key.provider.name}} {{:: 'publicKey' | translate}}