KEYCLOAK-7701 Refactor key providers to support additional algorithms
This commit is contained in:
parent
a5d155a35a
commit
3c5027de3c
53 changed files with 832 additions and 815 deletions
27
server-spi-private/src/main/java/org/keycloak/keys/HmacKeyProvider.java → core/src/main/java/org/keycloak/crypto/Algorithm.java
Normal file → Executable file
27
server-spi-private/src/main/java/org/keycloak/keys/HmacKeyProvider.java → core/src/main/java/org/keycloak/crypto/Algorithm.java
Normal file → Executable file
|
@ -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 <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||
*/
|
||||
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";
|
||||
}
|
42
core/src/main/java/org/keycloak/crypto/JavaAlgorithm.java
Normal file
42
core/src/main/java/org/keycloak/crypto/JavaAlgorithm.java
Normal file
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -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 <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
public interface AesKeyProvider extends SecretKeyProvider {
|
||||
|
||||
default AlgorithmType getType() {
|
||||
return AlgorithmType.AES;
|
||||
public static KeyStatus from(boolean active, boolean enabled) {
|
||||
if (!enabled) {
|
||||
return KeyStatus.DISABLED;
|
||||
} else {
|
||||
return active ? KeyStatus.ACTIVE : KeyStatus.PASSIVE;
|
||||
}
|
||||
}
|
||||
|
||||
default String getJavaAlgorithmName() {
|
||||
return "AES";
|
||||
public boolean isActive() {
|
||||
return this.equals(ACTIVE);
|
||||
}
|
||||
|
||||
public boolean isEnabled() {
|
||||
return this.equals(ACTIVE) || this.equals(PASSIVE);
|
||||
}
|
||||
|
||||
}
|
|
@ -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 <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||
*/
|
||||
public interface RsaKeyProviderFactory extends KeyProviderFactory {
|
||||
|
||||
@Override
|
||||
default Map<String, Object> getTypeMetadata() {
|
||||
return Collections.singletonMap("algorithmType", AlgorithmType.RSA);
|
||||
}
|
||||
String EC = "EC";
|
||||
String RSA = "RSA";
|
||||
String OCT = "OCT";
|
||||
|
||||
}
|
|
@ -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 <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||
*/
|
||||
public interface HmacKeyProviderFactory extends KeyProviderFactory<HmacKeyProvider> {
|
||||
|
||||
@Override
|
||||
default Map<String, Object> getTypeMetadata() {
|
||||
return Collections.singletonMap("algorithmType", AlgorithmType.HMAC);
|
||||
}
|
||||
SIG,
|
||||
ENC
|
||||
|
||||
}
|
135
core/src/main/java/org/keycloak/crypto/KeyWrapper.java
Normal file
135
core/src/main/java/org/keycloak/crypto/KeyWrapper.java
Normal file
|
@ -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<String> 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<String> getAlgorithms() {
|
||||
return algorithms;
|
||||
}
|
||||
|
||||
public void setAlgorithms(String... algorithms) {
|
||||
this.algorithms = new HashSet<>();
|
||||
for (String a : algorithms) {
|
||||
this.algorithms.add(a);
|
||||
}
|
||||
}
|
||||
|
||||
public void setAlgorithms(Set<String> 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;
|
||||
}
|
||||
}
|
|
@ -19,6 +19,7 @@ package org.keycloak.representations.idm;
|
|||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||
|
@ -54,6 +55,7 @@ public class KeysMetadataRepresentation {
|
|||
private String status;
|
||||
|
||||
private String type;
|
||||
private Set<String> algorithms;
|
||||
|
||||
private String publicKey;
|
||||
private String certificate;
|
||||
|
@ -98,6 +100,14 @@ public class KeysMetadataRepresentation {
|
|||
this.type = type;
|
||||
}
|
||||
|
||||
public Set<String> getAlgorithms() {
|
||||
return algorithms;
|
||||
}
|
||||
|
||||
public void setAlgorithms(Set<String> algorithms) {
|
||||
this.algorithms = algorithms;
|
||||
}
|
||||
|
||||
public String getPublicKey() {
|
||||
return publicKey;
|
||||
}
|
||||
|
|
|
@ -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 <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
public interface AesKeyProviderFactory extends KeyProviderFactory<AesKeyProvider> {
|
||||
|
||||
@Override
|
||||
default Map<String, Object> getTypeMetadata() {
|
||||
return Collections.singletonMap("algorithmType", AlgorithmType.AES);
|
||||
}
|
||||
}
|
|
@ -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 @@ import java.util.List;
|
|||
/**
|
||||
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||
*/
|
||||
public interface KeyProvider<T extends KeyMetadata> extends Provider {
|
||||
public interface KeyProvider extends Provider {
|
||||
|
||||
/**
|
||||
* Returns the algorithm type the keys can be used for
|
||||
*
|
||||
* Returns the key
|
||||
* @return
|
||||
*/
|
||||
AlgorithmType getType();
|
||||
List<KeyWrapper> getKeys();
|
||||
|
||||
/**
|
||||
* Return the KID for the active keypair, or <code>null</code> if no active key is available.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
String getKid();
|
||||
|
||||
/**
|
||||
* Return metadata about all keypairs held by the provider
|
||||
* @return
|
||||
*/
|
||||
List<T> getKeyMetadata();
|
||||
default void close() {
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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 <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||
|
@ -28,4 +30,16 @@ public interface KeyProviderFactory<T extends KeyProvider> 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() {
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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 <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||
*/
|
||||
public interface RsaKeyProvider extends KeyProvider<RsaKeyMetadata> {
|
||||
|
||||
default AlgorithmType getType() {
|
||||
return AlgorithmType.RSA;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the private key for the active keypair, or <code>null</code> if no active key is available.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
PrivateKey getPrivateKey();
|
||||
|
||||
/**
|
||||
* Return the public key for the specified kid, or <code>null</code> if the kid is unknown.
|
||||
*
|
||||
* @param kid
|
||||
* @return
|
||||
*/
|
||||
PublicKey getPublicKey(String kid);
|
||||
|
||||
/**
|
||||
* Return the certificate for the specified kid, or <code>null</code> if the kid is unknown.
|
||||
*
|
||||
* @param kid
|
||||
* @return
|
||||
*/
|
||||
X509Certificate getCertificate(String kid);
|
||||
|
||||
}
|
|
@ -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 <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
public interface SecretKeyProvider extends KeyProvider<SecretKeyMetadata> {
|
||||
|
||||
/**
|
||||
* Return the active secret key, or <code>null</code> if no active key is available.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
SecretKey getSecretKey();
|
||||
|
||||
/**
|
||||
* Return the secret key for the specified kid, or <code>null</code> 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();
|
||||
}
|
|
@ -17,21 +17,19 @@
|
|||
|
||||
package org.keycloak.keys;
|
||||
|
||||
import org.keycloak.crypto.KeyStatus;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||
*/
|
||||
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 abstract class KeyMetadata {
|
|||
this.kid = kid;
|
||||
}
|
||||
|
||||
public Status getStatus() {
|
||||
public KeyStatus getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
public void setStatus(Status status) {
|
||||
public void setStatus(KeyStatus status) {
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
|
|
|
@ -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 @@ import java.util.List;
|
|||
*/
|
||||
public interface KeyManager {
|
||||
|
||||
KeyWrapper getActiveKey(RealmModel realm, KeyUse use, String algorithm);
|
||||
|
||||
KeyWrapper getKey(RealmModel realm, String kid, KeyUse use, String algorithm);
|
||||
|
||||
List<KeyWrapper> getKeys(RealmModel realm);
|
||||
|
||||
List<KeyWrapper> 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<RsaKeyMetadata> getRsaKeys(RealmModel realm, boolean includeDisabled);
|
||||
@Deprecated
|
||||
List<RsaKeyMetadata> getRsaKeys(RealmModel realm);
|
||||
|
||||
@Deprecated
|
||||
ActiveHmacKey getActiveHmacKey(RealmModel realm);
|
||||
|
||||
@Deprecated
|
||||
SecretKey getHmacSecretKey(RealmModel realm, String kid);
|
||||
|
||||
List<SecretKeyMetadata> getHmacKeys(RealmModel realm, boolean includeDisabled);
|
||||
@Deprecated
|
||||
List<SecretKeyMetadata> getHmacKeys(RealmModel realm);
|
||||
|
||||
@Deprecated
|
||||
ActiveAesKey getActiveAesKey(RealmModel realm);
|
||||
|
||||
@Deprecated
|
||||
SecretKey getAesSecretKey(RealmModel realm, String kid);
|
||||
|
||||
List<SecretKeyMetadata> getAesKeys(RealmModel realm, boolean includeDisabled);
|
||||
@Deprecated
|
||||
List<SecretKeyMetadata> getAesKeys(RealmModel realm);
|
||||
|
||||
class ActiveRsaKey {
|
||||
private final String kid;
|
||||
|
|
|
@ -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 javax.ws.rs.core.UriInfo;
|
|||
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 class SAMLIdentityProvider extends AbstractIdentityProvider<SAMLIdentityP
|
|||
StringBuilder encryptionKeysString = new StringBuilder();
|
||||
Set<RsaKeyMetadata> 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());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 @@ import java.util.List;
|
|||
/**
|
||||
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||
*/
|
||||
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);
|
||||
protected abstract KeyWrapper loadKey(RealmModel realm, ComponentModel model);
|
||||
|
||||
@Override
|
||||
public final String getKid() {
|
||||
return isActive() ? keys.getKid() : null;
|
||||
public List<KeyWrapper> getKeys() {
|
||||
return Collections.singletonList(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final PrivateKey getPrivateKey() {
|
||||
return isActive() ? keys.getKeyPair().getPrivate() : null;
|
||||
}
|
||||
protected KeyWrapper createKeyWrapper(KeyPair keyPair, X509Certificate certificate) {
|
||||
KeyWrapper key = new KeyWrapper();
|
||||
|
||||
@Override
|
||||
public final PublicKey getPublicKey(String kid) {
|
||||
return isEnabled() && kid.equals(keys.getKid()) ? keys.getKeyPair().getPublic() : null;
|
||||
}
|
||||
key.setProviderId(model.getId());
|
||||
key.setProviderPriority(model.get("priority", 0l));
|
||||
|
||||
@Override
|
||||
public X509Certificate getCertificate(String kid) {
|
||||
return isEnabled() && kid.equals(keys.getKid()) ? keys.getCertificate() : 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(status);
|
||||
key.setSignKey(keyPair.getPrivate());
|
||||
key.setVerifyKey(keyPair.getPublic());
|
||||
key.setCertificate(certificate);
|
||||
|
||||
@Override
|
||||
public final List<RsaKeyMetadata> 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();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
}
|
||||
|
||||
private boolean isEnabled() {
|
||||
return keys != null && enabled;
|
||||
}
|
||||
|
||||
private boolean isActive() {
|
||||
return isEnabled() && active;
|
||||
}
|
||||
|
||||
public static class Keys {
|
||||
private String kid;
|
||||
private KeyPair keyPair;
|
||||
private X509Certificate certificate;
|
||||
|
||||
public Keys(String kid, KeyPair keyPair, X509Certificate certificate) {
|
||||
this.kid = kid;
|
||||
this.keyPair = keyPair;
|
||||
this.certificate = certificate;
|
||||
}
|
||||
|
||||
public String getKid() {
|
||||
return kid;
|
||||
}
|
||||
|
||||
public KeyPair getKeyPair() {
|
||||
return keyPair;
|
||||
}
|
||||
|
||||
public X509Certificate getCertificate() {
|
||||
return certificate;
|
||||
}
|
||||
return key;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -27,7 +27,7 @@ import org.keycloak.provider.ProviderConfigurationBuilder;
|
|||
/**
|
||||
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||
*/
|
||||
public abstract class AbstractRsaKeyProviderFactory implements RsaKeyProviderFactory {
|
||||
public abstract class AbstractRsaKeyProviderFactory implements KeyProviderFactory {
|
||||
|
||||
public final static ProviderConfigurationBuilder configurationBuilder() {
|
||||
return ProviderConfigurationBuilder.create()
|
||||
|
|
|
@ -19,20 +19,19 @@ package org.keycloak.keys;
|
|||
|
||||
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 <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||
|
@ -49,204 +48,172 @@ public class DefaultKeyManager implements KeyManager {
|
|||
}
|
||||
|
||||
@Override
|
||||
public KeyWrapper getActiveKey(RealmModel realm, KeyUse use, String algorithm) {
|
||||
for (KeyProvider p : getProviders(realm)) {
|
||||
for (KeyWrapper key : p .getKeys()) {
|
||||
if (key.getStatus().isActive() && matches(key, use, algorithm)) {
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.tracev("Active key found: realm={0} kid={1} algorithm={2}", realm.getName(), key.getKid(), algorithm);
|
||||
}
|
||||
|
||||
return key;
|
||||
}
|
||||
}
|
||||
}
|
||||
throw new RuntimeException("Failed to find key: realm=" + realm.getName() + " algorithm=" + algorithm);
|
||||
}
|
||||
|
||||
@Override
|
||||
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);
|
||||
return null;
|
||||
}
|
||||
|
||||
for (KeyProvider p : getProviders(realm)) {
|
||||
for (KeyWrapper key : p.getKeys()) {
|
||||
if (key.getKid().equals(kid) && key.getStatus().isEnabled() && matches(key, use, algorithm)) {
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.tracev("Active key realm={0} kid={1} algorithm={2}", realm.getName(), key.getKid(), algorithm);
|
||||
}
|
||||
|
||||
return key;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.tracev("Failed to find public key realm={0} kid={1} algorithm={2}", realm.getName(), kid, algorithm);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<KeyWrapper> getKeys(RealmModel realm, KeyUse use, String algorithm) {
|
||||
List<KeyWrapper> keys = new LinkedList<>();
|
||||
for (KeyProvider p : getProviders(realm)) {
|
||||
for (KeyWrapper key : p .getKeys()) {
|
||||
if (key.getStatus().isEnabled() && matches(key, use, algorithm)) {
|
||||
keys.add(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
return keys;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<KeyWrapper> getKeys(RealmModel realm) {
|
||||
List<KeyWrapper> keys = new LinkedList<>();
|
||||
for (KeyProvider p : getProviders(realm)) {
|
||||
for (KeyWrapper key : p .getKeys()) {
|
||||
keys.add(key);
|
||||
}
|
||||
}
|
||||
return keys;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Deprecated
|
||||
public ActiveRsaKey getActiveRsaKey(RealmModel realm) {
|
||||
for (KeyProvider p : getProviders(realm)) {
|
||||
if (p.getType().equals(AlgorithmType.RSA)) {
|
||||
RsaKeyProvider r = (RsaKeyProvider) p;
|
||||
if (r.getKid() != null && r.getPrivateKey() != null) {
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.tracev("Active key realm={0} kid={1}", realm.getName(), p.getKid());
|
||||
}
|
||||
String kid = p.getKid();
|
||||
return new ActiveRsaKey(kid, r.getPrivateKey(), r.getPublicKey(kid), r.getCertificate(kid));
|
||||
}
|
||||
}
|
||||
}
|
||||
throw new RuntimeException("Failed to get RSA keys");
|
||||
KeyWrapper key = getActiveKey(realm, KeyUse.SIG, Algorithm.RS256);
|
||||
return new ActiveRsaKey(key.getKid(), (PrivateKey) key.getSignKey(), (PublicKey) key.getVerifyKey(), key.getCertificate());
|
||||
}
|
||||
|
||||
@Override
|
||||
@Deprecated
|
||||
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");
|
||||
KeyWrapper key = getActiveKey(realm, KeyUse.SIG, Algorithm.HS256);
|
||||
return new ActiveHmacKey(key.getKid(), key.getSecretKey());
|
||||
}
|
||||
|
||||
@Override
|
||||
@Deprecated
|
||||
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());
|
||||
}
|
||||
}
|
||||
}
|
||||
throw new RuntimeException("Failed to get keys");
|
||||
KeyWrapper key = getActiveKey(realm, KeyUse.ENC, Algorithm.AES);
|
||||
return new ActiveAesKey(key.getKid(), key.getSecretKey());
|
||||
}
|
||||
|
||||
@Override
|
||||
@Deprecated
|
||||
public PublicKey getRsaPublicKey(RealmModel realm, String kid) {
|
||||
if (kid == null) {
|
||||
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) {
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.tracev("Found public key realm={0} kid={1}", realm.getName(), kid);
|
||||
}
|
||||
return publicKey;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.tracev("Failed to find public key realm={0} kid={1}", realm.getName(), kid);
|
||||
}
|
||||
return null;
|
||||
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) {
|
||||
if (kid == null) {
|
||||
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;
|
||||
Certificate certificate = r.getCertificate(kid);
|
||||
if (certificate != null) {
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.tracev("Found certificate realm={0} kid={1}", realm.getName(), kid);
|
||||
}
|
||||
return certificate;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.tracev("Failed to find certificate realm={0} kid={1}", realm.getName(), kid);
|
||||
}
|
||||
return null;
|
||||
KeyWrapper key = getKey(realm, kid, KeyUse.SIG, Algorithm.RS256);
|
||||
return key != null ? key.getCertificate() : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Deprecated
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.tracev("Failed to find secret key realm={0} kid={1}", realm.getName(), kid);
|
||||
}
|
||||
return null;
|
||||
KeyWrapper key = getKey(realm, kid, KeyUse.SIG, Algorithm.HS256);
|
||||
return key != null ? key.getSecretKey() : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Deprecated
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
KeyWrapper key = getKey(realm, kid, KeyUse.ENC, Algorithm.AES);
|
||||
return key.getSecretKey();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<RsaKeyMetadata> getRsaKeys(RealmModel realm, boolean includeDisabled) {
|
||||
@Deprecated
|
||||
public List<RsaKeyMetadata> getRsaKeys(RealmModel realm) {
|
||||
List<RsaKeyMetadata> keys = new LinkedList<>();
|
||||
for (KeyProvider p : getProviders(realm)) {
|
||||
if (p instanceof RsaKeyProvider) {
|
||||
if (includeDisabled) {
|
||||
keys.addAll(p.getKeyMetadata());
|
||||
} else {
|
||||
List<RsaKeyMetadata> 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<SecretKeyMetadata> getHmacKeys(RealmModel realm, boolean includeDisabled) {
|
||||
public List<SecretKeyMetadata> getHmacKeys(RealmModel realm) {
|
||||
List<SecretKeyMetadata> keys = new LinkedList<>();
|
||||
for (KeyProvider p : getProviders(realm)) {
|
||||
if (p instanceof HmacKeyProvider) {
|
||||
if (includeDisabled) {
|
||||
keys.addAll(p.getKeyMetadata());
|
||||
} else {
|
||||
List<SecretKeyMetadata> 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<SecretKeyMetadata> getAesKeys(RealmModel realm, boolean includeDisabled) {
|
||||
public List<SecretKeyMetadata> getAesKeys(RealmModel realm) {
|
||||
List<SecretKeyMetadata> keys = new LinkedList<>();
|
||||
for (KeyProvider p : getProviders(realm)) {
|
||||
if (p instanceof AesKeyProvider) {
|
||||
if (includeDisabled) {
|
||||
keys.addAll(p.getKeyMetadata());
|
||||
} else {
|
||||
List<SecretKeyMetadata> 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<KeyProvider> getProviders(RealmModel realm) {
|
||||
List<KeyProvider> providers = providersMap.get(realm.getId());
|
||||
if (providers == null) {
|
||||
|
@ -255,10 +222,6 @@ public class DefaultKeyManager implements KeyManager {
|
|||
List<ComponentModel> 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<KeyProvider> f = session.getKeycloakSessionFactory().getProviderFactory(KeyProvider.class, c.getProviderId());
|
||||
|
@ -266,41 +229,30 @@ public class DefaultKeyManager implements KeyManager {
|
|||
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;
|
||||
}
|
||||
|
|
|
@ -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 <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
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;
|
||||
|
|
|
@ -20,6 +20,10 @@ package org.keycloak.keys;
|
|||
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 @@ import java.util.List;
|
|||
/**
|
||||
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -20,77 +20,62 @@ package org.keycloak.keys;
|
|||
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 <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||
*/
|
||||
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<KeyWrapper> getKeys() {
|
||||
return Collections.singletonList(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PrivateKey getPrivateKey() {
|
||||
return keyPair.getPrivate();
|
||||
private KeyWrapper createKeyWrapper() {
|
||||
KeyPair keyPair = KeyUtils.generateRsaKeyPair(2048);
|
||||
|
||||
KeyWrapper key = new KeyWrapper();
|
||||
|
||||
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());
|
||||
|
||||
return key;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PublicKey getPublicKey(String kid) {
|
||||
return kid.equals(this.kid) ? keyPair.getPublic() : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public X509Certificate getCertificate(String kid) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<RsaKeyMetadata> getKeyMetadata() {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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 <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||
*/
|
||||
public abstract class FailsafeSecretKeyProvider implements SecretKeyProvider {
|
||||
public abstract class FailsafeSecretKeyProvider implements KeyProvider {
|
||||
|
||||
|
||||
private static String KID;
|
||||
|
||||
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<KeyWrapper> 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<SecretKeyMetadata> getKeyMetadata() {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
protected abstract String getType();
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
}
|
||||
protected abstract String getAlgorithm();
|
||||
|
||||
protected abstract Logger logger();
|
||||
}
|
||||
|
|
|
@ -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 <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -21,6 +21,7 @@ import java.util.List;
|
|||
|
||||
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 @@ import static org.keycloak.provider.ProviderConfigProperty.LIST_TYPE;
|
|||
/**
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
public class GeneratedAesKeyProviderFactory extends GeneratedSecretKeyProviderFactory<AesKeyProvider> implements AesKeyProviderFactory {
|
||||
public class GeneratedAesKeyProviderFactory extends GeneratedSecretKeyProviderFactory<GeneratedSecretKeyProvider> {
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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 <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||
*/
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -34,7 +34,7 @@ import java.util.List;
|
|||
/**
|
||||
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||
*/
|
||||
public class GeneratedHmacKeyProviderFactory extends GeneratedSecretKeyProviderFactory<HmacKeyProvider> implements HmacKeyProviderFactory {
|
||||
public class GeneratedHmacKeyProviderFactory extends GeneratedSecretKeyProviderFactory<GeneratedHmacKeyProvider> {
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
|
|
|
@ -110,18 +110,6 @@ public class GeneratedRsaKeyProviderFactory extends AbstractRsaKeyProviderFactor
|
|||
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;
|
||||
|
|
|
@ -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 <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||
*/
|
||||
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<KeyWrapper> 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<SecretKeyMetadata> 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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -66,17 +66,5 @@ public abstract class GeneratedSecretKeyProviderFactory<T extends KeyProvider> i
|
|||
|
||||
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();
|
||||
}
|
||||
|
|
|
@ -20,6 +20,7 @@ package org.keycloak.keys;
|
|||
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 class ImportedRsaKeyProvider extends AbstractRsaKeyProvider {
|
|||
}
|
||||
|
||||
@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 class ImportedRsaKeyProvider extends AbstractRsaKeyProvider {
|
|||
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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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 class ImportedRsaKeyProviderFactory extends AbstractRsaKeyProviderFactory
|
|||
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;
|
||||
|
|
|
@ -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 @@ import java.security.cert.X509Certificate;
|
|||
*/
|
||||
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 @@ public class JavaKeystoreKeyProvider extends AbstractRsaKeyProvider {
|
|||
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) {
|
||||
|
|
|
@ -77,7 +77,7 @@ public class JavaKeystoreKeyProviderFactory extends AbstractRsaKeyProviderFactor
|
|||
|
||||
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 class JavaKeystoreKeyProviderFactory extends AbstractRsaKeyProviderFactor
|
|||
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;
|
||||
|
|
|
@ -26,7 +26,6 @@ import org.keycloak.forms.login.LoginFormsProvider;
|
|||
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 class OIDCLoginProtocolService {
|
|||
@Produces(MediaType.APPLICATION_JSON)
|
||||
@NoCache
|
||||
public Response certs() {
|
||||
List<RsaKeyMetadata> publicKeys = session.keys().getRsaKeys(realm, false);
|
||||
List<RsaKeyMetadata> publicKeys = session.keys().getRsaKeys(realm);
|
||||
JWK[] keys = new JWK[publicKeys.size()];
|
||||
|
||||
int i = 0;
|
||||
|
|
|
@ -23,6 +23,7 @@ import org.jboss.resteasy.spi.ResteasyProviderFactory;
|
|||
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.Set;
|
|||
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 class SamlService extends AuthorizationEndpointBase {
|
|||
StringBuilder keysString = new StringBuilder();
|
||||
Set<RsaKeyMetadata> 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());
|
||||
}
|
||||
|
|
|
@ -19,6 +19,8 @@ package org.keycloak.protocol.saml.installation;
|
|||
|
||||
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.models.RealmModel;
|
|||
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 javax.ws.rs.core.UriBuilder;
|
|||
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 <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
|
@ -90,8 +90,8 @@ public class SamlIDPDescriptorClientInstallation implements ClientInstallationPr
|
|||
// keys
|
||||
Set<RsaKeyMetadata> 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());
|
||||
}
|
||||
|
|
|
@ -19,9 +19,9 @@ package org.keycloak.services.resources.admin;
|
|||
|
||||
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 class KeyResource {
|
|||
public KeysMetadataRepresentation getKeyMetadata() {
|
||||
auth.realm().requireViewRealm();
|
||||
|
||||
KeyManager keystore = session.keys();
|
||||
|
||||
KeysMetadataRepresentation keys = new KeysMetadataRepresentation();
|
||||
keys.setKeys(new LinkedList<>());
|
||||
keys.setActive(new HashMap<>());
|
||||
|
||||
Map<String, String> 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);
|
||||
for (KeyWrapper key : session.keys().getKeys(realm)) {
|
||||
KeysMetadataRepresentation.KeyMetadataRepresentation r = new KeysMetadataRepresentation.KeyMetadataRepresentation();
|
||||
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);
|
||||
|
||||
List<KeysMetadataRepresentation.KeyMetadataRepresentation> 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);
|
||||
if (key.getStatus().isActive()) {
|
||||
for (String a : key.getAlgorithms()) {
|
||||
if (!keys.getActive().containsKey(a)) {
|
||||
keys.getActive().put(a, key.getKid());
|
||||
}
|
||||
}
|
||||
for (SecretKeyMetadata m : session.keys().getHmacKeys(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.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);
|
||||
}
|
||||
|
||||
keys.setKeys(l);
|
||||
|
||||
return keys;
|
||||
}
|
||||
|
|
|
@ -23,6 +23,7 @@ import org.keycloak.admin.client.resource.ClientScopeResource;
|
|||
import org.keycloak.admin.client.resource.RealmResource;
|
||||
import org.keycloak.admin.client.resource.RoleResource;
|
||||
import org.keycloak.admin.client.resource.UserResource;
|
||||
import org.keycloak.crypto.Algorithm;
|
||||
import org.keycloak.representations.idm.ClientRepresentation;
|
||||
import org.keycloak.representations.idm.ClientScopeRepresentation;
|
||||
import org.keycloak.representations.idm.CredentialRepresentation;
|
||||
|
@ -255,7 +256,7 @@ public class ApiUtil {
|
|||
|
||||
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;
|
||||
|
|
|
@ -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 class KeyUtils {
|
|||
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");
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -38,6 +38,7 @@ import org.keycloak.common.VerificationException;
|
|||
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 class OAuthClient {
|
|||
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)) {
|
||||
|
|
|
@ -42,7 +42,7 @@ import org.keycloak.common.util.MultivaluedHashMap;
|
|||
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 @@ public class OIDCPublicKeyRotationAdapterTest extends AbstractServletsAdapterTes
|
|||
|
||||
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();
|
||||
|
|
|
@ -28,6 +28,7 @@ import org.junit.Test;
|
|||
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 class KcOIDCBrokerWithSignatureTest extends AbstractBaseBrokerTest {
|
|||
|
||||
|
||||
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 @@ public class KcOIDCBrokerWithSignatureTest extends AbstractBaseBrokerTest {
|
|||
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);
|
||||
}
|
||||
|
||||
|
|
|
@ -2,10 +2,12 @@ package org.keycloak.testsuite.broker;
|
|||
|
||||
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.arquillian.SuiteContext;
|
|||
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 class KcSamlSignedBrokerTest extends KcSamlBrokerTest {
|
|||
public List<ClientRepresentation> createProviderClients(SuiteContext suiteContext) {
|
||||
List<ClientRepresentation> 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 class KcSamlSignedBrokerTest extends KcSamlBrokerTest {
|
|||
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<String, String> config = result.getConfig();
|
||||
|
@ -121,10 +124,10 @@ public class KcSamlSignedBrokerTest extends KcSamlBrokerTest {
|
|||
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)
|
||||
|
|
|
@ -24,6 +24,8 @@ import org.junit.Rule;
|
|||
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 class GeneratedHmacKeyProviderTest extends AbstractKeycloakTest {
|
|||
|
||||
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 class GeneratedHmacKeyProviderTest extends AbstractKeycloakTest {
|
|||
|
||||
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));
|
||||
|
|
|
@ -24,6 +24,7 @@ import org.keycloak.common.util.CertificateUtils;
|
|||
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 class ImportedRsaKeyProviderTest extends AbstractKeycloakTest {
|
|||
|
||||
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);
|
||||
|
||||
|
|
|
@ -30,6 +30,7 @@ import org.keycloak.common.VerificationException;
|
|||
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 class KeyRotationTest extends AbstractKeycloakTest {
|
|||
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 class KeyRotationTest extends AbstractKeycloakTest {
|
|||
@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 class KeyRotationTest extends AbstractKeycloakTest {
|
|||
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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 @@ import java.net.URI;
|
|||
*/
|
||||
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)) {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -289,8 +289,23 @@ 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();
|
||||
},
|
||||
serverInfo : function(ServerInfoLoader) {
|
||||
return ServerInfoLoader();
|
||||
},
|
||||
keys: function(RealmKeysLoader) {
|
||||
return RealmKeysLoader();
|
||||
}
|
||||
},
|
||||
controller : 'RealmKeysCtrl'
|
||||
})
|
||||
.when('/realms/:realm/keys/disabled', {
|
||||
templateUrl : resourceUrl + '/partials/realm-keys-disabled.html',
|
||||
resolve : {
|
||||
realm : function(RealmLoader) {
|
||||
return RealmLoader();
|
||||
|
|
|
@ -0,0 +1,71 @@
|
|||
<!--
|
||||
~ 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.
|
||||
-->
|
||||
|
||||
<div class="col-sm-9 col-md-10 col-sm-push-3 col-md-push-2">
|
||||
<kc-tabs-realm></kc-tabs-realm>
|
||||
|
||||
<ul class="nav nav-tabs nav-tabs-pf">
|
||||
<li><a href="#/realms/{{realm.realm}}/keys">{{:: 'active' | translate}}</a></li>
|
||||
<li><a href="#/realms/{{realm.realm}}/keys/passive">{{:: 'passive' | translate}}</a></li>
|
||||
<li class="active"><a href="#/realms/{{realm.realm}}/keys/disabled">{{:: 'disabled' | translate}}</a></li>
|
||||
<li><a href="#/realms/{{realm.realm}}/keys/providers">{{:: 'providers' | translate}}</a></li>
|
||||
</ul>
|
||||
|
||||
<table class="table table-striped table-bordered">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="kc-table-actions" colspan="7">
|
||||
<div class="form-inline">
|
||||
<div class="form-group">
|
||||
<div class="input-group">
|
||||
<input type="text" placeholder="{{:: 'search.placeholder' | translate}}" data-ng-model="search" class="form-control search"">
|
||||
<div class="input-group-addon">
|
||||
<i class="fa fa-search"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>{{:: 'algorithms' | translate}}</th>
|
||||
<th>{{:: 'type' | translate}}</th>
|
||||
<th>{{:: 'kid' | translate}}</th>
|
||||
<th>{{:: 'priority' | translate}}</th>
|
||||
<th>{{:: 'provider' | translate}}</th>
|
||||
<th colspan="2">{{:: 'publicKeys' | translate}}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr ng-repeat="key in keys | filter:search | filter:{status:'DISABLED'}">
|
||||
<td>{{key.algorithm.sort().join(', ')}}</td>
|
||||
<td>{{key.type}}</td>
|
||||
<td>{{key.kid}}</td>
|
||||
<td>{{key.providerPriority}}</td>
|
||||
<td><a href="#/realms/{{realm.realm}}/keys/providers/{{key.provider.providerId}}/{{key.provider.id}}">{{key.provider.name}}</a></td>
|
||||
|
||||
<td data-ng-show="key.type === 'RSA'" class="kc-action-cell" data-ng-click="viewKey(key.publicKey)">{{:: 'publicKey' | translate}}</td>
|
||||
<td data-ng-show="key.type === 'RSA'" class="kc-action-cell" data-ng-click="viewKey(key.certificate)">{{:: 'certificate' | translate}}</td>
|
||||
|
||||
<td data-ng-show="key.type !== 'RSA'" colspan="2"></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
</div>
|
||||
|
||||
<kc-menu></kc-menu>
|
|
@ -20,15 +20,30 @@
|
|||
|
||||
<ul class="nav nav-tabs nav-tabs-pf">
|
||||
<li><a href="#/realms/{{realm.realm}}/keys">{{:: 'active' | translate}}</a></li>
|
||||
<li class="active"><a href="#/realms/{{realm.realm}}/keys/list">{{:: 'all' | translate}}</a></li>
|
||||
<li class="active"><a href="#/realms/{{realm.realm}}/keys/passive">{{:: 'passive' | translate}}</a></li>
|
||||
<li><a href="#/realms/{{realm.realm}}/keys/disabled">{{:: 'disabled' | translate}}</a></li>
|
||||
<li><a href="#/realms/{{realm.realm}}/keys/providers">{{:: 'providers' | translate}}</a></li>
|
||||
</ul>
|
||||
|
||||
<table class="table table-striped table-bordered">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="kc-table-actions" colspan="7">
|
||||
<div class="form-inline">
|
||||
<div class="form-group">
|
||||
<div class="input-group">
|
||||
<input type="text" placeholder="{{:: 'search.placeholder' | translate}}" data-ng-model="search" class="form-control search"">
|
||||
<div class="input-group-addon">
|
||||
<i class="fa fa-search"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>{{:: 'algorithms' | translate}}</th>
|
||||
<th>{{:: 'type' | translate}}</th>
|
||||
<th>{{:: 'status' | translate}}</th>
|
||||
<th>{{:: 'kid' | translate}}</th>
|
||||
<th>{{:: 'priority' | translate}}</th>
|
||||
<th>{{:: 'provider' | translate}}</th>
|
||||
|
@ -36,9 +51,9 @@
|
|||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr ng-repeat="key in keys">
|
||||
<tr ng-repeat="key in keys | filter:search | filter:{status:'PASSIVE'}">
|
||||
<td>{{key.algorithm.sort().join(', ')}}</td>
|
||||
<td>{{key.type}}</td>
|
||||
<td>{{key.status}}</td>
|
||||
<td>{{key.kid}}</td>
|
||||
<td>{{key.providerPriority}}</td>
|
||||
<td><a href="#/realms/{{realm.realm}}/keys/providers/{{key.provider.providerId}}/{{key.provider.id}}">{{key.provider.name}}</a></td>
|
|
@ -20,16 +20,26 @@
|
|||
|
||||
<ul class="nav nav-tabs nav-tabs-pf">
|
||||
<li><a href="#/realms/{{realm.realm}}/keys">{{:: 'active' | translate}}</a></li>
|
||||
<li><a href="#/realms/{{realm.realm}}/keys/list">{{:: 'all' | translate}}</a></li>
|
||||
<li><a href="#/realms/{{realm.realm}}/keys/passive">{{:: 'passive' | translate}}</a></li>
|
||||
<li><a href="#/realms/{{realm.realm}}/keys/disabled">{{:: 'disabled' | translate}}</a></li>
|
||||
<li class="active"><a href="#/realms/{{realm.realm}}/keys/providers">{{:: 'providers' | translate}}</a></li>
|
||||
</ul>
|
||||
|
||||
<table class="table table-striped table-bordered">
|
||||
<thead>
|
||||
<tr ng-show="providers.length > 0 && access.manageRealm">
|
||||
<th colspan="7" class="kc-table-actions">
|
||||
<div class="pull-right">
|
||||
<div>
|
||||
<tr>
|
||||
<th class="kc-table-actions" colspan="7">
|
||||
<div class="form-inline">
|
||||
<div class="form-group">
|
||||
<div class="input-group">
|
||||
<input type="text" placeholder="{{:: 'search.placeholder' | translate}}" data-ng-model="search" class="form-control search">
|
||||
<div class="input-group-addon">
|
||||
<i class="fa fa-search"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="pull-right" data-ng-show="access.manageClients">
|
||||
<select class="form-control" ng-model="selectedProvider"
|
||||
ng-options="p.id for p in providers"
|
||||
data-ng-change="addProvider(selectedProvider); selectedProvider = null">
|
||||
|
@ -40,20 +50,18 @@
|
|||
</th>
|
||||
</tr>
|
||||
<tr data-ng-show="instances && instances.length > 0">
|
||||
<th>{{:: 'type' | translate}}</th>
|
||||
<th>{{:: 'name' | translate}}</th>
|
||||
<th>{{:: 'id' | translate}}</th>
|
||||
<th>{{:: 'provider' | translate}}</th>
|
||||
<th>{{:: 'providerHelpText' | translate}}</th>
|
||||
<th>{{:: 'priority' | translate}}</th>
|
||||
<th colspan="2">{{:: 'actions' | translate}}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr ng-repeat="instance in instances">
|
||||
<td>{{instance.provider.metadata.algorithmType}}</td>
|
||||
<td>{{instance.name}}</td>
|
||||
<td><a href="#/realms/{{realm.realm}}/keys/providers/{{instance.providerId}}/{{instance.id}}">{{instance.id}}</a></td>
|
||||
<tr ng-repeat="instance in instances | filter:search">
|
||||
<td><a href="#/realms/{{realm.realm}}/keys/providers/{{instance.providerId}}/{{instance.id}}">{{instance.name}}</a></td>
|
||||
<td>{{instance.providerId}}</td>
|
||||
<td>{{instance.provider.helpText}}</td>
|
||||
<td>{{instance.config['priority'][0]}}</td>
|
||||
<td class="kc-action-cell" kc-open="/realms/{{realm.realm}}/keys/providers/{{instance.providerId}}/{{instance.id}}">{{:: 'edit' | translate}}</td>
|
||||
<td class="kc-action-cell" ng-show="instances.length > 1" data-ng-click="removeInstance(instance)">{{:: 'delete' | translate}}</td>
|
||||
|
|
|
@ -1,25 +1,61 @@
|
|||
<!--
|
||||
~ 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.
|
||||
-->
|
||||
|
||||
<div class="col-sm-9 col-md-10 col-sm-push-3 col-md-push-2">
|
||||
<kc-tabs-realm></kc-tabs-realm>
|
||||
|
||||
<ul class="nav nav-tabs nav-tabs-pf">
|
||||
<li class="active"><a href="#/realms/{{realm.realm}}/keys">{{:: 'active' | translate}}</a></li>
|
||||
<li><a href="#/realms/{{realm.realm}}/keys/list">{{:: 'all' | translate}}</a></li>
|
||||
<li><a href="#/realms/{{realm.realm}}/keys/passive">{{:: 'passive' | translate}}</a></li>
|
||||
<li><a href="#/realms/{{realm.realm}}/keys/disabled">{{:: 'disabled' | translate}}</a></li>
|
||||
<li><a href="#/realms/{{realm.realm}}/keys/providers">{{:: 'providers' | translate}}</a></li>
|
||||
</ul>
|
||||
|
||||
<table class="table table-striped table-bordered">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="kc-table-actions" colspan="7">
|
||||
<div class="form-inline">
|
||||
<div class="form-group">
|
||||
<div class="input-group">
|
||||
<input type="text" placeholder="{{:: 'search.placeholder' | translate}}" data-ng-model="search" class="form-control search"">
|
||||
<div class="input-group-addon">
|
||||
<i class="fa fa-search"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>{{:: 'algorithms' | translate}}</th>
|
||||
<th>{{:: 'type' | translate}}</th>
|
||||
<th>{{:: 'kid' | translate}}</th>
|
||||
<th>{{:: 'priority' | translate}}</th>
|
||||
<th>{{:: 'provider' | translate}}</th>
|
||||
<th colspan="2">{{:: 'publicKeys' | translate}}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr ng-repeat="key in active">
|
||||
<tr ng-repeat="key in keys | filter:search | filter:{status:'ACTIVE'}">
|
||||
<td>{{key.algorithm.sort().join(', ')}}</td>
|
||||
<td>{{key.type}}</td>
|
||||
<td>{{key.kid}}</td>
|
||||
<td>{{key.providerPriority}}</td>
|
||||
<td><a href="#/realms/{{realm.realm}}/keys/providers/{{key.provider.providerId}}/{{key.provider.id}}">{{key.provider.name}}</a></td>
|
||||
|
||||
<td data-ng-show="key.type === 'RSA'" class="kc-action-cell" data-ng-click="viewKey(key.publicKey)">{{:: 'publicKey' | translate}}</td>
|
||||
|
|
Loading…
Reference in a new issue