keycloak-uncached

KEYCLOAK-8460 Request Object Signature Verification Other

11/19/2018 11:28:32 AM

Changes

Details

diff --git a/core/src/main/java/org/keycloak/jose/jwk/ECPublicJWK.java b/core/src/main/java/org/keycloak/jose/jwk/ECPublicJWK.java
index 19644aa..d97c7e8 100644
--- a/core/src/main/java/org/keycloak/jose/jwk/ECPublicJWK.java
+++ b/core/src/main/java/org/keycloak/jose/jwk/ECPublicJWK.java
@@ -24,6 +24,8 @@ import com.fasterxml.jackson.annotation.JsonProperty;
  */
 public class ECPublicJWK extends JWK {
 
+    public static final String EC = "EC";
+
     public static final String CRV = "crv";
     public static final String X = "x";
     public static final String Y = "y";
diff --git a/core/src/main/java/org/keycloak/jose/jwk/JWKParser.java b/core/src/main/java/org/keycloak/jose/jwk/JWKParser.java
index b27c48c..db59973 100755
--- a/core/src/main/java/org/keycloak/jose/jwk/JWKParser.java
+++ b/core/src/main/java/org/keycloak/jose/jwk/JWKParser.java
@@ -128,7 +128,7 @@ public class JWKParser {
     }
 
     public boolean isKeyTypeSupported(String keyType) {
-        return RSAPublicJWK.RSA.equals(keyType);
+        return (RSAPublicJWK.RSA.equals(keyType) || ECPublicJWK.EC.equals(keyType));
     }
 
 }
diff --git a/core/src/main/java/org/keycloak/util/JWKSUtils.java b/core/src/main/java/org/keycloak/util/JWKSUtils.java
index 8db20e8..1745239 100644
--- a/core/src/main/java/org/keycloak/util/JWKSUtils.java
+++ b/core/src/main/java/org/keycloak/util/JWKSUtils.java
@@ -17,6 +17,8 @@
 
 package org.keycloak.util;
 
+import org.keycloak.crypto.KeyUse;
+import org.keycloak.crypto.KeyWrapper;
 import org.keycloak.jose.jwk.JSONWebKeySet;
 import org.keycloak.jose.jwk.JWK;
 import org.keycloak.jose.jwk.JWKParser;
@@ -43,6 +45,34 @@ public class JWKSUtils {
         return result;
     }
 
+    public static Map<String, KeyWrapper> getKeyWrappersForUse(JSONWebKeySet keySet, JWK.Use requestedUse) {
+        Map<String, KeyWrapper> result = new HashMap<>();
+        for (JWK jwk : keySet.getKeys()) {
+            JWKParser parser = JWKParser.create(jwk);
+            if (jwk.getPublicKeyUse().equals(requestedUse.asString()) && parser.isKeyTypeSupported(jwk.getKeyType())) {
+                KeyWrapper keyWrapper = new KeyWrapper();
+                keyWrapper.setKid(jwk.getKeyId());
+                keyWrapper.setAlgorithm(jwk.getAlgorithm());
+                keyWrapper.setType(jwk.getKeyType());
+                keyWrapper.setUse(getKeyUse(jwk.getPublicKeyUse()));
+                keyWrapper.setVerifyKey(parser.toPublicKey());
+                result.put(keyWrapper.getKid(), keyWrapper);
+            }
+        }
+        return result;
+    }
+
+    private static KeyUse getKeyUse(String keyUse) {
+        switch (keyUse) {
+            case "sig" : 
+                return KeyUse.SIG;
+            case "enc" : 
+                return KeyUse.ENC;
+            default :
+                return null;
+        }
+    }
+
     public static JWK getKeyForUse(JSONWebKeySet keySet, JWK.Use requestedUse) {
         for (JWK jwk : keySet.getKeys()) {
             JWKParser parser = JWKParser.create(jwk);
diff --git a/model/infinispan/src/main/java/org/keycloak/keys/infinispan/InfinispanPublicKeyStorageProvider.java b/model/infinispan/src/main/java/org/keycloak/keys/infinispan/InfinispanPublicKeyStorageProvider.java
index 933b923..230baec 100644
--- a/model/infinispan/src/main/java/org/keycloak/keys/infinispan/InfinispanPublicKeyStorageProvider.java
+++ b/model/infinispan/src/main/java/org/keycloak/keys/infinispan/InfinispanPublicKeyStorageProvider.java
@@ -17,7 +17,6 @@
 
 package org.keycloak.keys.infinispan;
 
-import java.security.PublicKey;
 import java.util.Collections;
 import java.util.HashSet;
 import java.util.Map;
@@ -30,6 +29,7 @@ import org.infinispan.Cache;
 import org.jboss.logging.Logger;
 import org.keycloak.cluster.ClusterProvider;
 import org.keycloak.common.util.Time;
+import org.keycloak.crypto.KeyWrapper;
 import org.keycloak.keys.PublicKeyLoader;
 import org.keycloak.keys.PublicKeyStorageProvider;
 import org.keycloak.models.KeycloakSession;
@@ -127,11 +127,11 @@ public class InfinispanPublicKeyStorageProvider implements PublicKeyStorageProvi
 
 
     @Override
-    public PublicKey getPublicKey(String modelKey, String kid, PublicKeyLoader loader) {
+    public KeyWrapper getPublicKey(String modelKey, String kid, PublicKeyLoader loader) {
         // Check if key is in cache
         PublicKeysEntry entry = keys.get(modelKey);
         if (entry != null) {
-            PublicKey publicKey = getPublicKey(entry.getCurrentKeys(), kid);
+            KeyWrapper publicKey = getPublicKey(entry.getCurrentKeys(), kid);
             if (publicKey != null) {
                 return publicKey;
             }
@@ -157,7 +157,7 @@ public class InfinispanPublicKeyStorageProvider implements PublicKeyStorageProvi
                 entry = task.get();
 
                 // Computation finished. Let's see if key is available
-                PublicKey publicKey = getPublicKey(entry.getCurrentKeys(), kid);
+                KeyWrapper publicKey = getPublicKey(entry.getCurrentKeys(), kid);
                 if (publicKey != null) {
                     return publicKey;
                 }
@@ -182,7 +182,7 @@ public class InfinispanPublicKeyStorageProvider implements PublicKeyStorageProvi
         return null;
     }
 
-    private PublicKey getPublicKey(Map<String, PublicKey> publicKeys, String kid) {
+    private KeyWrapper getPublicKey(Map<String, KeyWrapper> publicKeys, String kid) {
         // Backwards compatibility
         if (kid == null && !publicKeys.isEmpty()) {
             return publicKeys.values().iterator().next();
@@ -218,7 +218,7 @@ public class InfinispanPublicKeyStorageProvider implements PublicKeyStorageProvi
             // Check again if we are allowed to send request. There is a chance other task was already finished and removed from tasksInProgress in the meantime.
             if (currentTime > lastRequestTime + minTimeBetweenRequests) {
 
-                Map<String, PublicKey> publicKeys = delegate.loadKeys();
+                Map<String, KeyWrapper> publicKeys = delegate.loadKeys();
 
                 if (log.isDebugEnabled()) {
                     log.debugf("Public keys retrieved successfully for model %s. New kids: %s", modelKey, publicKeys.keySet().toString());
diff --git a/model/infinispan/src/main/java/org/keycloak/keys/infinispan/PublicKeysEntry.java b/model/infinispan/src/main/java/org/keycloak/keys/infinispan/PublicKeysEntry.java
index cb0561c..2f2d807 100644
--- a/model/infinispan/src/main/java/org/keycloak/keys/infinispan/PublicKeysEntry.java
+++ b/model/infinispan/src/main/java/org/keycloak/keys/infinispan/PublicKeysEntry.java
@@ -18,9 +18,10 @@
 package org.keycloak.keys.infinispan;
 
 import java.io.Serializable;
-import java.security.PublicKey;
 import java.util.Map;
 
+import org.keycloak.crypto.KeyWrapper;
+
 /**
  * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
  */
@@ -28,9 +29,9 @@ public class PublicKeysEntry implements Serializable {
 
     private final int lastRequestTime;
 
-    private final Map<String, PublicKey> currentKeys;
+    private final Map<String, KeyWrapper> currentKeys;
 
-    public PublicKeysEntry(int lastRequestTime, Map<String, PublicKey> currentKeys) {
+    public PublicKeysEntry(int lastRequestTime, Map<String, KeyWrapper> currentKeys) {
         this.lastRequestTime = lastRequestTime;
         this.currentKeys = currentKeys;
     }
@@ -39,7 +40,7 @@ public class PublicKeysEntry implements Serializable {
         return lastRequestTime;
     }
 
-    public Map<String, PublicKey> getCurrentKeys() {
+    public Map<String, KeyWrapper> getCurrentKeys() {
         return currentKeys;
     }
 }
diff --git a/model/infinispan/src/test/java/org/keycloak/keys/infinispan/InfinispanKeyStorageProviderTest.java b/model/infinispan/src/test/java/org/keycloak/keys/infinispan/InfinispanKeyStorageProviderTest.java
index 308d7b2..b68d653 100644
--- a/model/infinispan/src/test/java/org/keycloak/keys/infinispan/InfinispanKeyStorageProviderTest.java
+++ b/model/infinispan/src/test/java/org/keycloak/keys/infinispan/InfinispanKeyStorageProviderTest.java
@@ -17,7 +17,6 @@
 
 package org.keycloak.keys.infinispan;
 
-import java.security.PublicKey;
 import java.util.Collections;
 import java.util.LinkedList;
 import java.util.List;
@@ -39,6 +38,7 @@ import org.junit.Before;
 import org.junit.Test;
 import org.keycloak.common.util.Time;
 import org.keycloak.connections.infinispan.InfinispanConnectionProvider;
+import org.keycloak.crypto.KeyWrapper;
 import org.keycloak.keys.PublicKeyLoader;
 
 /**
@@ -144,7 +144,7 @@ public class InfinispanKeyStorageProviderTest {
         }
 
         @Override
-        public Map<String, PublicKey> loadKeys() throws Exception {
+        public Map<String, KeyWrapper> loadKeys() throws Exception {
             counters.putIfAbsent(modelKey, new AtomicInteger(0));
             AtomicInteger currentCounter = counters.get(modelKey);
 
diff --git a/server-spi/src/main/java/org/keycloak/models/TokenManager.java b/server-spi/src/main/java/org/keycloak/models/TokenManager.java
index 4666e57..bd0244f 100644
--- a/server-spi/src/main/java/org/keycloak/models/TokenManager.java
+++ b/server-spi/src/main/java/org/keycloak/models/TokenManager.java
@@ -41,4 +41,6 @@ public interface TokenManager {
 
     String signatureAlgorithm(TokenCategory category);
 
+    <T> T decodeClientJWT(String token, ClientModel client, Class<T> clazz);
+
 }
diff --git a/server-spi-private/src/main/java/org/keycloak/crypto/ClientSignatureVerifierProvider.java b/server-spi-private/src/main/java/org/keycloak/crypto/ClientSignatureVerifierProvider.java
new file mode 100644
index 0000000..a4224cb
--- /dev/null
+++ b/server-spi-private/src/main/java/org/keycloak/crypto/ClientSignatureVerifierProvider.java
@@ -0,0 +1,31 @@
+/*
+ * 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 org.keycloak.common.VerificationException;
+import org.keycloak.jose.jws.JWSInput;
+import org.keycloak.models.ClientModel;
+import org.keycloak.provider.Provider;
+
+public interface ClientSignatureVerifierProvider extends Provider {
+    SignatureVerifierContext verifier(ClientModel client, JWSInput input) throws VerificationException;
+
+    @Override
+    default void close() {
+    }
+}
diff --git a/server-spi-private/src/main/java/org/keycloak/crypto/ClientSignatureVerifierProviderFactory.java b/server-spi-private/src/main/java/org/keycloak/crypto/ClientSignatureVerifierProviderFactory.java
new file mode 100644
index 0000000..d8fd4cd
--- /dev/null
+++ b/server-spi-private/src/main/java/org/keycloak/crypto/ClientSignatureVerifierProviderFactory.java
@@ -0,0 +1,38 @@
+/*
+ * 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 org.keycloak.Config;
+import org.keycloak.models.KeycloakSessionFactory;
+import org.keycloak.provider.ProviderFactory;
+
+public interface ClientSignatureVerifierProviderFactory extends ProviderFactory<ClientSignatureVerifierProvider> {
+
+    @Override
+    default void init(Config.Scope config) {
+    }
+
+    @Override
+    default void postInit(KeycloakSessionFactory factory) {
+    }
+
+    @Override
+    default void close() {
+    }
+
+}
diff --git a/server-spi-private/src/main/java/org/keycloak/crypto/ClientSignatureVerifierSpi.java b/server-spi-private/src/main/java/org/keycloak/crypto/ClientSignatureVerifierSpi.java
new file mode 100644
index 0000000..0461f85
--- /dev/null
+++ b/server-spi-private/src/main/java/org/keycloak/crypto/ClientSignatureVerifierSpi.java
@@ -0,0 +1,44 @@
+/*
+ * 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 org.keycloak.provider.Provider;
+import org.keycloak.provider.ProviderFactory;
+import org.keycloak.provider.Spi;
+
+public class ClientSignatureVerifierSpi implements Spi {
+
+    @Override
+    public boolean isInternal() {
+        return true;
+    }
+
+    @Override
+    public String getName() {
+        return "clientSignature";
+    }
+
+    @Override
+    public Class<? extends Provider> getProviderClass() {
+        return ClientSignatureVerifierProvider.class;
+    }
+
+    @Override
+    public Class<? extends ProviderFactory> getProviderFactoryClass() {
+        return ClientSignatureVerifierProviderFactory.class;
+    }
+}
diff --git a/server-spi-private/src/main/java/org/keycloak/keys/PublicKeyLoader.java b/server-spi-private/src/main/java/org/keycloak/keys/PublicKeyLoader.java
index c4ec672..3ec53f8 100644
--- a/server-spi-private/src/main/java/org/keycloak/keys/PublicKeyLoader.java
+++ b/server-spi-private/src/main/java/org/keycloak/keys/PublicKeyLoader.java
@@ -17,14 +17,15 @@
 
 package org.keycloak.keys;
 
-import java.security.PublicKey;
 import java.util.Map;
 
+import org.keycloak.crypto.KeyWrapper;
+
 /**
  * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
  */
 public interface PublicKeyLoader {
 
-    Map<String, PublicKey> loadKeys() throws Exception;
+    Map<String, KeyWrapper> loadKeys() throws Exception;
 
 }
diff --git a/server-spi-private/src/main/java/org/keycloak/keys/PublicKeyStorageProvider.java b/server-spi-private/src/main/java/org/keycloak/keys/PublicKeyStorageProvider.java
index 1d72180..437ad23 100644
--- a/server-spi-private/src/main/java/org/keycloak/keys/PublicKeyStorageProvider.java
+++ b/server-spi-private/src/main/java/org/keycloak/keys/PublicKeyStorageProvider.java
@@ -17,8 +17,7 @@
 
 package org.keycloak.keys;
 
-import java.security.PublicKey;
-
+import org.keycloak.crypto.KeyWrapper;
 import org.keycloak.provider.Provider;
 
 /**
@@ -35,7 +34,7 @@ public interface PublicKeyStorageProvider extends Provider {
      * @param loader
      * @return
      */
-    PublicKey getPublicKey(String modelKey, String kid, PublicKeyLoader loader);
+	KeyWrapper getPublicKey(String modelKey, String kid, PublicKeyLoader loader);
 
     /**
      * Clears all the cached public keys, so they need to be loaded again
diff --git a/server-spi-private/src/main/resources/META-INF/services/org.keycloak.provider.Spi b/server-spi-private/src/main/resources/META-INF/services/org.keycloak.provider.Spi
index 751b8cc..560d15a 100755
--- a/server-spi-private/src/main/resources/META-INF/services/org.keycloak.provider.Spi
+++ b/server-spi-private/src/main/resources/META-INF/services/org.keycloak.provider.Spi
@@ -72,4 +72,5 @@ org.keycloak.credential.CredentialSpi
 org.keycloak.keys.PublicKeyStorageSpi
 org.keycloak.keys.KeySpi
 org.keycloak.storage.client.ClientStorageProviderSpi
-org.keycloak.crypto.SignatureSpi
\ No newline at end of file
+org.keycloak.crypto.SignatureSpi
+org.keycloak.crypto.ClientSignatureVerifierSpi
\ No newline at end of file
diff --git a/services/src/main/java/org/keycloak/crypto/AsymmetricClientSignatureVerifierProvider.java b/services/src/main/java/org/keycloak/crypto/AsymmetricClientSignatureVerifierProvider.java
new file mode 100644
index 0000000..fc54945
--- /dev/null
+++ b/services/src/main/java/org/keycloak/crypto/AsymmetricClientSignatureVerifierProvider.java
@@ -0,0 +1,38 @@
+/*
+ * 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 org.keycloak.common.VerificationException;
+import org.keycloak.jose.jws.JWSInput;
+import org.keycloak.models.ClientModel;
+import org.keycloak.models.KeycloakSession;
+
+public class AsymmetricClientSignatureVerifierProvider implements ClientSignatureVerifierProvider {
+    private final KeycloakSession session;
+    private final String algorithm;
+
+    public AsymmetricClientSignatureVerifierProvider(KeycloakSession session, String algorithm) {
+        this.session = session;
+        this.algorithm = algorithm;
+    }
+
+    @Override
+    public SignatureVerifierContext verifier(ClientModel client, JWSInput input) throws VerificationException {
+        return new ClientAsymmetricSignatureVerifierContext(session, client, input);
+    }
+
+}
diff --git a/services/src/main/java/org/keycloak/crypto/ClientAsymmetricSignatureVerifierContext.java b/services/src/main/java/org/keycloak/crypto/ClientAsymmetricSignatureVerifierContext.java
new file mode 100644
index 0000000..317c363
--- /dev/null
+++ b/services/src/main/java/org/keycloak/crypto/ClientAsymmetricSignatureVerifierContext.java
@@ -0,0 +1,38 @@
+/*
+ * 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 org.keycloak.common.VerificationException;
+import org.keycloak.jose.jws.JWSInput;
+import org.keycloak.keys.loader.PublicKeyStorageManager;
+import org.keycloak.models.ClientModel;
+import org.keycloak.models.KeycloakSession;
+
+public class ClientAsymmetricSignatureVerifierContext extends AsymmetricSignatureVerifierContext {
+
+    public ClientAsymmetricSignatureVerifierContext(KeycloakSession session, ClientModel client, JWSInput input) throws VerificationException {
+        super(getKey(session, client, input));
+    }
+
+    private static KeyWrapper getKey(KeycloakSession session, ClientModel client, JWSInput input) throws VerificationException {
+        KeyWrapper key = PublicKeyStorageManager.getClientPublicKeyWrapper(session, client, input);
+        if (key == null) {
+            throw new VerificationException("Key not found");
+        }
+        return key;
+    }
+}
diff --git a/services/src/main/java/org/keycloak/crypto/ES256ClientSignatureVerifierProviderFactory.java b/services/src/main/java/org/keycloak/crypto/ES256ClientSignatureVerifierProviderFactory.java
new file mode 100644
index 0000000..c03f040
--- /dev/null
+++ b/services/src/main/java/org/keycloak/crypto/ES256ClientSignatureVerifierProviderFactory.java
@@ -0,0 +1,35 @@
+/*
+ * 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 org.keycloak.models.KeycloakSession;
+
+public class ES256ClientSignatureVerifierProviderFactory implements ClientSignatureVerifierProviderFactory {
+
+    public static final String ID = Algorithm.ES256;
+
+    @Override
+    public String getId() {
+        return ID;
+    }
+
+    @Override
+    public ClientSignatureVerifierProvider create(KeycloakSession session) {
+        return new AsymmetricClientSignatureVerifierProvider(session, Algorithm.ES256);
+    }
+
+}
diff --git a/services/src/main/java/org/keycloak/crypto/ES384ClientSignatureVerifierProviderFactory.java b/services/src/main/java/org/keycloak/crypto/ES384ClientSignatureVerifierProviderFactory.java
new file mode 100644
index 0000000..715daf5
--- /dev/null
+++ b/services/src/main/java/org/keycloak/crypto/ES384ClientSignatureVerifierProviderFactory.java
@@ -0,0 +1,35 @@
+/*
+ * 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 org.keycloak.models.KeycloakSession;
+
+public class ES384ClientSignatureVerifierProviderFactory implements ClientSignatureVerifierProviderFactory {
+
+    public static final String ID = Algorithm.ES384;
+
+    @Override
+    public String getId() {
+        return ID;
+    }
+
+    @Override
+    public ClientSignatureVerifierProvider create(KeycloakSession session) {
+        return new AsymmetricClientSignatureVerifierProvider(session, Algorithm.ES384);
+    }
+
+}
diff --git a/services/src/main/java/org/keycloak/crypto/ES512ClientSignatureVerifierProviderFactory.java b/services/src/main/java/org/keycloak/crypto/ES512ClientSignatureVerifierProviderFactory.java
new file mode 100644
index 0000000..cb76657
--- /dev/null
+++ b/services/src/main/java/org/keycloak/crypto/ES512ClientSignatureVerifierProviderFactory.java
@@ -0,0 +1,35 @@
+/*
+ * 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 org.keycloak.models.KeycloakSession;
+
+public class ES512ClientSignatureVerifierProviderFactory  implements ClientSignatureVerifierProviderFactory {
+
+    public static final String ID = Algorithm.ES512;
+
+    @Override
+    public String getId() {
+        return ID;
+    }
+
+    @Override
+    public ClientSignatureVerifierProvider create(KeycloakSession session) {
+        return new AsymmetricClientSignatureVerifierProvider(session, Algorithm.ES512);
+    }
+
+}
diff --git a/services/src/main/java/org/keycloak/crypto/RS256ClientSignatureVerifierProviderFactory.java b/services/src/main/java/org/keycloak/crypto/RS256ClientSignatureVerifierProviderFactory.java
new file mode 100644
index 0000000..5fc1a74
--- /dev/null
+++ b/services/src/main/java/org/keycloak/crypto/RS256ClientSignatureVerifierProviderFactory.java
@@ -0,0 +1,35 @@
+/*
+ * 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 org.keycloak.models.KeycloakSession;
+
+public class RS256ClientSignatureVerifierProviderFactory implements ClientSignatureVerifierProviderFactory {
+
+    public static final String ID = Algorithm.RS256;
+
+    @Override
+    public String getId() {
+        return ID;
+    }
+
+    @Override
+    public ClientSignatureVerifierProvider create(KeycloakSession session) {
+        return new AsymmetricClientSignatureVerifierProvider(session, Algorithm.RS256);
+    }
+
+}
diff --git a/services/src/main/java/org/keycloak/crypto/RS384ClientSignatureVerifierProviderFactory.java b/services/src/main/java/org/keycloak/crypto/RS384ClientSignatureVerifierProviderFactory.java
new file mode 100644
index 0000000..6c1f5c0
--- /dev/null
+++ b/services/src/main/java/org/keycloak/crypto/RS384ClientSignatureVerifierProviderFactory.java
@@ -0,0 +1,35 @@
+/*
+ * 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 org.keycloak.models.KeycloakSession;
+
+public class RS384ClientSignatureVerifierProviderFactory implements ClientSignatureVerifierProviderFactory {
+
+    public static final String ID = Algorithm.RS384;
+
+    @Override
+    public String getId() {
+        return ID;
+    }
+
+    @Override
+    public ClientSignatureVerifierProvider create(KeycloakSession session) {
+        return new AsymmetricClientSignatureVerifierProvider(session, Algorithm.RS384);
+    }
+
+}
diff --git a/services/src/main/java/org/keycloak/crypto/RS512ClientSignatureVerifierProviderFactory.java b/services/src/main/java/org/keycloak/crypto/RS512ClientSignatureVerifierProviderFactory.java
new file mode 100644
index 0000000..efeedb1
--- /dev/null
+++ b/services/src/main/java/org/keycloak/crypto/RS512ClientSignatureVerifierProviderFactory.java
@@ -0,0 +1,35 @@
+/*
+ * 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 org.keycloak.models.KeycloakSession;
+
+public class RS512ClientSignatureVerifierProviderFactory implements ClientSignatureVerifierProviderFactory {
+
+    public static final String ID = Algorithm.RS512;
+
+    @Override
+    public String getId() {
+        return ID;
+    }
+
+    @Override
+    public ClientSignatureVerifierProvider create(KeycloakSession session) {
+        return new AsymmetricClientSignatureVerifierProvider(session, Algorithm.RS512);
+    }
+
+}
diff --git a/services/src/main/java/org/keycloak/jose/jws/DefaultTokenManager.java b/services/src/main/java/org/keycloak/jose/jws/DefaultTokenManager.java
index 9a0323d..df9c5e1 100644
--- a/services/src/main/java/org/keycloak/jose/jws/DefaultTokenManager.java
+++ b/services/src/main/java/org/keycloak/jose/jws/DefaultTokenManager.java
@@ -20,6 +20,7 @@ import org.jboss.logging.Logger;
 import org.keycloak.Token;
 import org.keycloak.TokenCategory;
 import org.keycloak.crypto.Algorithm;
+import org.keycloak.crypto.ClientSignatureVerifierProvider;
 import org.keycloak.crypto.KeyUse;
 import org.keycloak.crypto.SignatureProvider;
 import org.keycloak.crypto.SignatureSignerContext;
@@ -84,6 +85,29 @@ public class DefaultTokenManager implements TokenManager {
     }
 
     @Override
+    public <T> T decodeClientJWT(String token, ClientModel client, Class<T> clazz) {
+        if (token == null) {
+            return null;
+        }
+        try {
+            JWSInput jws = new JWSInput(token);
+
+            String signatureAlgorithm = jws.getHeader().getAlgorithm().name();
+
+            ClientSignatureVerifierProvider signatureProvider = session.getProvider(ClientSignatureVerifierProvider.class, signatureAlgorithm);
+            if (signatureProvider == null) {
+                return null;
+            }
+
+            boolean valid = signatureProvider.verifier(client, jws).verify(jws.getEncodedSignatureInput().getBytes("UTF-8"), jws.getSignature());
+            return valid ? jws.readJsonContent(clazz) : null;
+        } catch (Exception e) {
+            logger.debug("Failed to decode token", e);
+            return null;
+        }
+    }
+
+    @Override
     public String signatureAlgorithm(TokenCategory category) {
         switch (category) {
             case INTERNAL:
diff --git a/services/src/main/java/org/keycloak/keys/loader/ClientPublicKeyLoader.java b/services/src/main/java/org/keycloak/keys/loader/ClientPublicKeyLoader.java
index af2a99c..23f43d7 100644
--- a/services/src/main/java/org/keycloak/keys/loader/ClientPublicKeyLoader.java
+++ b/services/src/main/java/org/keycloak/keys/loader/ClientPublicKeyLoader.java
@@ -20,6 +20,10 @@ package org.keycloak.keys.loader;
 import org.jboss.logging.Logger;
 import org.keycloak.authentication.authenticators.client.JWTClientAuthenticator;
 import org.keycloak.common.util.KeyUtils;
+import org.keycloak.crypto.Algorithm;
+import org.keycloak.crypto.KeyType;
+import org.keycloak.crypto.KeyUse;
+import org.keycloak.crypto.KeyWrapper;
 import org.keycloak.jose.jwk.JSONWebKeySet;
 import org.keycloak.jose.jwk.JWK;
 import org.keycloak.keys.PublicKeyLoader;
@@ -56,21 +60,18 @@ public class ClientPublicKeyLoader implements PublicKeyLoader {
 
 
     @Override
-    public Map<String, PublicKey> loadKeys() throws Exception {
+    public Map<String, KeyWrapper> loadKeys() throws Exception {
         OIDCAdvancedConfigWrapper config = OIDCAdvancedConfigWrapper.fromClientModel(client);
         if (config.isUseJwksUrl()) {
             String jwksUrl = config.getJwksUrl();
             jwksUrl = ResolveRelative.resolveRelativeUri(session.getContext().getUri().getRequestUri(), client.getRootUrl(), jwksUrl);
             JSONWebKeySet jwks = JWKSHttpUtils.sendJwksRequest(session, jwksUrl);
-            return JWKSUtils.getKeysForUse(jwks, JWK.Use.SIG);
+            return JWKSUtils.getKeyWrappersForUse(jwks, JWK.Use.SIG);
         } else {
             try {
                 CertificateRepresentation certInfo = CertificateInfoHelper.getCertificateFromClient(client, JWTClientAuthenticator.ATTR_PREFIX);
-                PublicKey publicKey = getSignatureValidationKey(certInfo);
-
-                // Check if we have kid in DB, generate otherwise
-                String kid = certInfo.getKid() != null ? certInfo.getKid() : KeyUtils.createKeyId(publicKey);
-                return Collections.singletonMap(kid, publicKey);
+                KeyWrapper publicKey = getSignatureValidationKey(certInfo);
+                return Collections.singletonMap(publicKey.getKid(), publicKey);
             } catch (ModelException me) {
                 logger.warnf(me, "Unable to retrieve publicKey for verify signature of client '%s' . Error details: %s", client.getClientId(), me.getMessage());
                 return Collections.emptyMap();
@@ -79,7 +80,8 @@ public class ClientPublicKeyLoader implements PublicKeyLoader {
         }
     }
 
-    private static PublicKey getSignatureValidationKey(CertificateRepresentation certInfo) throws ModelException {
+    private static KeyWrapper getSignatureValidationKey(CertificateRepresentation certInfo) throws ModelException {
+        KeyWrapper keyWrapper = new KeyWrapper();
         String encodedCertificate = certInfo.getCertificate();
         String encodedPublicKey = certInfo.getPublicKey();
 
@@ -91,12 +93,25 @@ public class ClientPublicKeyLoader implements PublicKeyLoader {
             throw new ModelException("Client has both publicKey and certificate configured");
         }
 
+        keyWrapper.setAlgorithm(Algorithm.RS256);
+        keyWrapper.setType(KeyType.RSA);
+        keyWrapper.setUse(KeyUse.SIG);
+        String kid = null;
         if (encodedCertificate != null) {
             X509Certificate clientCert = KeycloakModelUtils.getCertificate(encodedCertificate);
-            return clientCert.getPublicKey();
+            // Check if we have kid in DB, generate otherwise
+            kid = certInfo.getKid() != null ? certInfo.getKid() : KeyUtils.createKeyId(clientCert.getPublicKey());
+            keyWrapper.setKid(kid);
+            keyWrapper.setVerifyKey(clientCert.getPublicKey());
+            keyWrapper.setCertificate(clientCert);
         } else {
-            return KeycloakModelUtils.getPublicKey(encodedPublicKey);
+            PublicKey publicKey = KeycloakModelUtils.getPublicKey(encodedPublicKey);
+            // Check if we have kid in DB, generate otherwise
+            kid = certInfo.getKid() != null ? certInfo.getKid() : KeyUtils.createKeyId(publicKey);
+            keyWrapper.setKid(kid);
+            keyWrapper.setVerifyKey(publicKey);
         }
+        return keyWrapper;
     }
 
 
diff --git a/services/src/main/java/org/keycloak/keys/loader/HardcodedPublicKeyLoader.java b/services/src/main/java/org/keycloak/keys/loader/HardcodedPublicKeyLoader.java
index 213694b..846559a 100644
--- a/services/src/main/java/org/keycloak/keys/loader/HardcodedPublicKeyLoader.java
+++ b/services/src/main/java/org/keycloak/keys/loader/HardcodedPublicKeyLoader.java
@@ -17,9 +17,12 @@
 package org.keycloak.keys.loader;
 
 import org.keycloak.common.util.PemUtils;
+import org.keycloak.crypto.Algorithm;
+import org.keycloak.crypto.KeyType;
+import org.keycloak.crypto.KeyUse;
+import org.keycloak.crypto.KeyWrapper;
 import org.keycloak.keys.PublicKeyLoader;
 
-import java.security.PublicKey;
 import java.util.Collections;
 import java.util.Map;
 
@@ -38,15 +41,20 @@ public class HardcodedPublicKeyLoader implements PublicKeyLoader {
     }
 
     @Override
-    public Map<String, PublicKey> loadKeys() throws Exception {
+    public Map<String, KeyWrapper> loadKeys() throws Exception {
         return Collections.unmodifiableMap(Collections.singletonMap(kid, getSavedPublicKey()));
     }
 
-    protected PublicKey getSavedPublicKey() {
+    protected KeyWrapper getSavedPublicKey() {
+        KeyWrapper keyWrapper = null;
         if (pem != null && ! pem.trim().equals("")) {
-            return PemUtils.decodePublicKey(pem);
-        } else {
-            return null;
+            keyWrapper = new KeyWrapper();
+            keyWrapper.setKid(kid);
+            keyWrapper.setType(KeyType.RSA);
+            keyWrapper.setAlgorithm(Algorithm.RS256);
+            keyWrapper.setUse(KeyUse.SIG);
+            keyWrapper.setVerifyKey(PemUtils.decodePublicKey(pem));
         }
+        return keyWrapper;
     }
 }
diff --git a/services/src/main/java/org/keycloak/keys/loader/OIDCIdentityProviderPublicKeyLoader.java b/services/src/main/java/org/keycloak/keys/loader/OIDCIdentityProviderPublicKeyLoader.java
index fda2c2f..7bc0ec0 100644
--- a/services/src/main/java/org/keycloak/keys/loader/OIDCIdentityProviderPublicKeyLoader.java
+++ b/services/src/main/java/org/keycloak/keys/loader/OIDCIdentityProviderPublicKeyLoader.java
@@ -21,6 +21,10 @@ import org.jboss.logging.Logger;
 import org.keycloak.broker.oidc.OIDCIdentityProviderConfig;
 import org.keycloak.common.util.KeyUtils;
 import org.keycloak.common.util.PemUtils;
+import org.keycloak.crypto.Algorithm;
+import org.keycloak.crypto.KeyType;
+import org.keycloak.crypto.KeyUse;
+import org.keycloak.crypto.KeyWrapper;
 import org.keycloak.jose.jwk.JSONWebKeySet;
 import org.keycloak.jose.jwk.JWK;
 import org.keycloak.keys.PublicKeyLoader;
@@ -48,23 +52,18 @@ public class OIDCIdentityProviderPublicKeyLoader implements PublicKeyLoader {
     }
 
     @Override
-    public Map<String, PublicKey> loadKeys() throws Exception {
+    public Map<String, KeyWrapper> loadKeys() throws Exception {
         if (config.isUseJwksUrl()) {
             String jwksUrl = config.getJwksUrl();
             JSONWebKeySet jwks = JWKSHttpUtils.sendJwksRequest(session, jwksUrl);
-            return JWKSUtils.getKeysForUse(jwks, JWK.Use.SIG);
+            return JWKSUtils.getKeyWrappersForUse(jwks, JWK.Use.SIG);
         } else {
             try {
-                PublicKey publicKey = getSavedPublicKey();
+            	KeyWrapper publicKey = getSavedPublicKey();
                 if (publicKey == null) {
                     return Collections.emptyMap();
                 }
-
-                String presetKeyId = config.getPublicKeySignatureVerifierKeyId();
-                String kid = (presetKeyId == null || presetKeyId.trim().isEmpty())
-                  ? KeyUtils.createKeyId(publicKey)
-                  : presetKeyId;
-                return Collections.singletonMap(kid, publicKey);
+                return Collections.singletonMap(publicKey.getKid(), publicKey);
             } catch (Exception e) {
                 logger.warnf(e, "Unable to retrieve publicKey for verify signature of identityProvider '%s' . Error details: %s", config.getAlias(), e.getMessage());
                 return Collections.emptyMap();
@@ -72,12 +71,23 @@ public class OIDCIdentityProviderPublicKeyLoader implements PublicKeyLoader {
         }
     }
 
-    protected PublicKey getSavedPublicKey() throws Exception {
+    protected KeyWrapper getSavedPublicKey() throws Exception {
+        KeyWrapper keyWrapper = null;
         if (config.getPublicKeySignatureVerifier() != null && !config.getPublicKeySignatureVerifier().trim().equals("")) {
-            return PemUtils.decodePublicKey(config.getPublicKeySignatureVerifier());
+            PublicKey publicKey = PemUtils.decodePublicKey(config.getPublicKeySignatureVerifier());
+            keyWrapper = new KeyWrapper();
+            String presetKeyId = config.getPublicKeySignatureVerifierKeyId();
+            String kid = (presetKeyId == null || presetKeyId.trim().isEmpty())
+              ? KeyUtils.createKeyId(publicKey)
+              : presetKeyId;
+            keyWrapper.setKid(kid);
+            keyWrapper.setType(KeyType.RSA);
+            keyWrapper.setAlgorithm(Algorithm.RS256);
+            keyWrapper.setUse(KeyUse.SIG);
+            keyWrapper.setVerifyKey(publicKey);
         } else {
             logger.warnf("No public key saved on identityProvider %s", config.getAlias());
-            return null;
         }
+        return keyWrapper;
     }
 }
diff --git a/services/src/main/java/org/keycloak/keys/loader/PublicKeyStorageManager.java b/services/src/main/java/org/keycloak/keys/loader/PublicKeyStorageManager.java
index 0f28ff8..b9e5222 100644
--- a/services/src/main/java/org/keycloak/keys/loader/PublicKeyStorageManager.java
+++ b/services/src/main/java/org/keycloak/keys/loader/PublicKeyStorageManager.java
@@ -19,6 +19,7 @@ package org.keycloak.keys.loader;
 
 import org.jboss.logging.Logger;
 import org.keycloak.broker.oidc.OIDCIdentityProviderConfig;
+import org.keycloak.crypto.KeyWrapper;
 import org.keycloak.jose.jws.JWSInput;
 import org.keycloak.keys.PublicKeyLoader;
 import org.keycloak.keys.PublicKeyStorageProvider;
@@ -37,16 +38,22 @@ public class PublicKeyStorageManager {
     private static final Logger logger = Logger.getLogger(PublicKeyStorageManager.class);
 
     public static PublicKey getClientPublicKey(KeycloakSession session, ClientModel client, JWSInput input) {
-        String kid = input.getHeader().getKeyId();
+        KeyWrapper keyWrapper = getClientPublicKeyWrapper(session, client, input);
+        PublicKey publicKey = null;
+        if (keyWrapper != null) {
+            publicKey = (PublicKey)keyWrapper.getVerifyKey();
+        }
+        return publicKey;
+    }
 
+    public static KeyWrapper getClientPublicKeyWrapper(KeycloakSession session, ClientModel client, JWSInput input) {
+        String kid = input.getHeader().getKeyId();
         PublicKeyStorageProvider keyStorage = session.getProvider(PublicKeyStorageProvider.class);
-
         String modelKey = PublicKeyStorageUtils.getClientModelCacheKey(client.getRealm().getId(), client.getId());
         ClientPublicKeyLoader loader = new ClientPublicKeyLoader(session, client);
         return keyStorage.getPublicKey(modelKey, kid, loader);
     }
 
-
     public static PublicKey getIdentityProviderPublicKey(KeycloakSession session, RealmModel realm, OIDCIdentityProviderConfig idpConfig, JWSInput input) {
         boolean keyIdSetInConfiguration = idpConfig.getPublicKeySignatureVerifierKeyId() != null
           && ! idpConfig.getPublicKeySignatureVerifierKeyId().trim().isEmpty();
@@ -73,6 +80,6 @@ public class PublicKeyStorageManager {
                 : kid, pem);
         }
 
-        return keyStorage.getPublicKey(modelKey, kid, loader);
+        return (PublicKey)keyStorage.getPublicKey(modelKey, kid, loader).getVerifyKey();
     }
 }
diff --git a/services/src/main/java/org/keycloak/protocol/oidc/endpoints/request/AuthzEndpointRequestObjectParser.java b/services/src/main/java/org/keycloak/protocol/oidc/endpoints/request/AuthzEndpointRequestObjectParser.java
index 6803680..42544db 100644
--- a/services/src/main/java/org/keycloak/protocol/oidc/endpoints/request/AuthzEndpointRequestObjectParser.java
+++ b/services/src/main/java/org/keycloak/protocol/oidc/endpoints/request/AuthzEndpointRequestObjectParser.java
@@ -17,16 +17,15 @@
 package org.keycloak.protocol.oidc.endpoints.request;
 
 import com.fasterxml.jackson.databind.JsonNode;
-import java.security.PublicKey;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Set;
 
+import org.keycloak.crypto.SignatureProvider;
+import org.keycloak.crypto.SignatureVerifierContext;
 import org.keycloak.jose.jws.Algorithm;
 import org.keycloak.jose.jws.JWSHeader;
 import org.keycloak.jose.jws.JWSInput;
-import org.keycloak.jose.jws.crypto.RSAProvider;
-import org.keycloak.keys.loader.PublicKeyStorageManager;
 import org.keycloak.models.ClientModel;
 import org.keycloak.models.KeycloakSession;
 import org.keycloak.protocol.oidc.OIDCAdvancedConfigWrapper;
@@ -44,29 +43,24 @@ class AuthzEndpointRequestObjectParser extends AuthzEndpointRequestParser {
     public AuthzEndpointRequestObjectParser(KeycloakSession session, String requestObject, ClientModel client) throws Exception {
         JWSInput input = new JWSInput(requestObject);
         JWSHeader header = input.getHeader();
+        Algorithm headerAlgorithm = header.getAlgorithm();
 
         Algorithm requestedSignatureAlgorithm = OIDCAdvancedConfigWrapper.fromClientModel(client).getRequestObjectSignatureAlg();
 
-        if (requestedSignatureAlgorithm != null && requestedSignatureAlgorithm != header.getAlgorithm()) {
+        if (headerAlgorithm == null) {
+            throw new RuntimeException("Request object signed algorithm not specified");
+        }
+        if (requestedSignatureAlgorithm != null && requestedSignatureAlgorithm != headerAlgorithm) {
             throw new RuntimeException("Request object signed with different algorithm than client requested algorithm");
         }
 
         if (header.getAlgorithm() == Algorithm.none) {
             this.requestParams = JsonSerialization.readValue(input.getContent(), JsonNode.class);
-        } else if (header.getAlgorithm() == Algorithm.RS256) {
-            PublicKey clientPublicKey = PublicKeyStorageManager.getClientPublicKey(session, client, input);
-            if (clientPublicKey == null) {
-                throw new RuntimeException("Client public key not found");
-            }
-
-            boolean verified = RSAProvider.verify(input, clientPublicKey);
-            if (!verified) {
-                throw new RuntimeException("Failed to verify signature on 'request' object");
-            }
-
-            this.requestParams = JsonSerialization.readValue(input.getContent(), JsonNode.class);
         } else {
-            throw new RuntimeException("Unsupported JWA algorithm used for signed request");
+            this.requestParams = session.tokens().decodeClientJWT(requestObject, client, JsonNode.class);
+            if (this.requestParams == null) {
+            	throw new RuntimeException("Failed to verify signature on 'request' object");
+            }
         }
     }
 
diff --git a/services/src/main/java/org/keycloak/protocol/oidc/OIDCWellKnownProvider.java b/services/src/main/java/org/keycloak/protocol/oidc/OIDCWellKnownProvider.java
index ec68cbd..db8bc54 100755
--- a/services/src/main/java/org/keycloak/protocol/oidc/OIDCWellKnownProvider.java
+++ b/services/src/main/java/org/keycloak/protocol/oidc/OIDCWellKnownProvider.java
@@ -20,6 +20,7 @@ package org.keycloak.protocol.oidc;
 import org.keycloak.OAuth2Constants;
 import org.keycloak.authentication.ClientAuthenticator;
 import org.keycloak.authentication.ClientAuthenticatorFactory;
+import org.keycloak.crypto.ClientSignatureVerifierProvider;
 import org.keycloak.crypto.SignatureProvider;
 import org.keycloak.jose.jws.Algorithm;
 import org.keycloak.models.ClientScopeModel;
@@ -46,8 +47,6 @@ import java.util.List;
  */
 public class OIDCWellKnownProvider implements WellKnownProvider {
 
-    public static final List<String> DEFAULT_REQUEST_OBJECT_SIGNING_ALG_VALUES_SUPPORTED  = list(Algorithm.none.toString(), Algorithm.RS256.toString());
-
     public static final List<String> DEFAULT_GRANT_TYPES_SUPPORTED = list(OAuth2Constants.AUTHORIZATION_CODE, OAuth2Constants.IMPLICIT, OAuth2Constants.REFRESH_TOKEN, OAuth2Constants.PASSWORD, OAuth2Constants.CLIENT_CREDENTIALS);
 
     public static final List<String> DEFAULT_RESPONSE_TYPES_SUPPORTED = list(OAuth2Constants.CODE, OIDCResponseType.NONE, OIDCResponseType.ID_TOKEN, OIDCResponseType.TOKEN, "id_token token", "code id_token", "code token", "code id_token token");
@@ -92,7 +91,7 @@ public class OIDCWellKnownProvider implements WellKnownProvider {
 
         config.setIdTokenSigningAlgValuesSupported(getSupportedSigningAlgorithms(false));
         config.setUserInfoSigningAlgValuesSupported(getSupportedSigningAlgorithms(true));
-        config.setRequestObjectSigningAlgValuesSupported(DEFAULT_REQUEST_OBJECT_SIGNING_ALG_VALUES_SUPPORTED);
+        config.setRequestObjectSigningAlgValuesSupported(getSupportedClientSigningAlgorithms(true));
         config.setResponseTypesSupported(DEFAULT_RESPONSE_TYPES_SUPPORTED);
         config.setSubjectTypesSupported(DEFAULT_SUBJECT_TYPES_SUPPORTED);
         config.setResponseModesSupported(DEFAULT_RESPONSE_MODES_SUPPORTED);
@@ -163,4 +162,14 @@ public class OIDCWellKnownProvider implements WellKnownProvider {
         return result;
     }
 
+    private List<String> getSupportedClientSigningAlgorithms(boolean includeNone) {
+        List<String> result = new LinkedList<>();
+        for (ProviderFactory s : session.getKeycloakSessionFactory().getProviderFactories(ClientSignatureVerifierProvider.class)) {
+            result.add(s.getId());
+        }
+        if (includeNone) {
+            result.add("none");
+        }
+        return result;
+    }
 }
diff --git a/services/src/main/resources/META-INF/services/org.keycloak.crypto.ClientSignatureVerifierProviderFactory b/services/src/main/resources/META-INF/services/org.keycloak.crypto.ClientSignatureVerifierProviderFactory
new file mode 100644
index 0000000..594281d
--- /dev/null
+++ b/services/src/main/resources/META-INF/services/org.keycloak.crypto.ClientSignatureVerifierProviderFactory
@@ -0,0 +1,6 @@
+org.keycloak.crypto.RS256ClientSignatureVerifierProviderFactory
+org.keycloak.crypto.RS384ClientSignatureVerifierProviderFactory
+org.keycloak.crypto.RS512ClientSignatureVerifierProviderFactory
+org.keycloak.crypto.ES256ClientSignatureVerifierProviderFactory
+org.keycloak.crypto.ES384ClientSignatureVerifierProviderFactory
+org.keycloak.crypto.ES512ClientSignatureVerifierProviderFactory
\ No newline at end of file
diff --git a/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/rest/resource/TestingOIDCEndpointsApplicationResource.java b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/rest/resource/TestingOIDCEndpointsApplicationResource.java
index fd35caf..3f5665e 100644
--- a/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/rest/resource/TestingOIDCEndpointsApplicationResource.java
+++ b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/rest/resource/TestingOIDCEndpointsApplicationResource.java
@@ -22,6 +22,10 @@ import org.jboss.resteasy.spi.BadRequestException;
 import org.keycloak.OAuth2Constants;
 import org.keycloak.common.util.KeyUtils;
 import org.keycloak.common.util.PemUtils;
+import org.keycloak.crypto.AsymmetricSignatureSignerContext;
+import org.keycloak.crypto.KeyType;
+import org.keycloak.crypto.KeyWrapper;
+import org.keycloak.crypto.SignatureSignerContext;
 import org.keycloak.jose.jwk.JSONWebKeySet;
 import org.keycloak.jose.jwk.JWK;
 import org.keycloak.jose.jwk.JWKBuilder;
@@ -36,10 +40,13 @@ import javax.ws.rs.Produces;
 import javax.ws.rs.QueryParam;
 import javax.ws.rs.core.MediaType;
 
+import java.security.InvalidAlgorithmParameterException;
 import java.security.KeyPair;
 import java.security.KeyPairGenerator;
 import java.security.NoSuchAlgorithmException;
 import java.security.PrivateKey;
+import java.security.SecureRandom;
+import java.security.spec.ECGenParameterSpec;
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
@@ -63,17 +70,52 @@ public class TestingOIDCEndpointsApplicationResource {
     @Produces(MediaType.APPLICATION_JSON)
     @Path("/generate-keys")
     @NoCache
-    public Map<String, String> generateKeys() {
+    public Map<String, String> generateKeys(@QueryParam("jwaAlgorithm") String jwaAlgorithm) {
         try {
-            KeyPair keyPair = KeyUtils.generateRsaKeyPair(2048);
+            KeyPair keyPair = null;
+            if (jwaAlgorithm == null) jwaAlgorithm = org.keycloak.crypto.Algorithm.RS256;
+            String keyType = null;
+
+            switch (jwaAlgorithm) {
+                case org.keycloak.crypto.Algorithm.RS256:
+                case org.keycloak.crypto.Algorithm.RS384:
+                case org.keycloak.crypto.Algorithm.RS512:
+                    keyType = KeyType.RSA;
+                    keyPair = KeyUtils.generateRsaKeyPair(2048);
+                    break;
+                case org.keycloak.crypto.Algorithm.ES256:
+                    keyType = KeyType.EC;
+                    keyPair = generateEcdsaKey("secp256r1");
+                    break;
+                case org.keycloak.crypto.Algorithm.ES384:
+                    keyType = KeyType.EC;
+                    keyPair = generateEcdsaKey("secp384r1");
+                    break;
+                case org.keycloak.crypto.Algorithm.ES512:
+                    keyType = KeyType.EC;
+                    keyPair = generateEcdsaKey("secp521r1");
+                    break;
+                default :
+                    throw new RuntimeException("Unsupported signature algorithm");
+            }
+
             clientData.setSigningKeyPair(keyPair);
+            clientData.setSigningKeyType(keyType);
+            clientData.setSigningKeyAlgorithm(jwaAlgorithm);
         } catch (Exception e) {
             throw new BadRequestException("Error generating signing keypair", e);
         }
-
         return getKeysAsPem();
     }
 
+    private KeyPair generateEcdsaKey(String ecDomainParamName) throws NoSuchAlgorithmException, InvalidAlgorithmParameterException {
+        KeyPairGenerator keyGen = KeyPairGenerator.getInstance("EC");
+        SecureRandom randomGen = SecureRandom.getInstance("SHA1PRNG");
+        ECGenParameterSpec ecSpec = new ECGenParameterSpec(ecDomainParamName);
+        keyGen.initialize(ecSpec, randomGen);
+        KeyPair keyPair = keyGen.generateKeyPair();
+        return keyPair;
+    }
 
     @GET
     @Produces(MediaType.APPLICATION_JSON)
@@ -95,11 +137,18 @@ public class TestingOIDCEndpointsApplicationResource {
     @NoCache
     public JSONWebKeySet getJwks() {
         JSONWebKeySet keySet = new JSONWebKeySet();
+        KeyPair signingKeyPair = clientData.getSigningKeyPair();
+        String signingKeyAlgorithm = clientData.getSigningKeyAlgorithm();
+        String signingKeyType = clientData.getSigningKeyType();
 
-        if (clientData.getSigningKeyPair() == null) {
+        if (signingKeyPair == null || !isSupportedSigningAlgorithm(signingKeyAlgorithm)) {
             keySet.setKeys(new JWK[] {});
+        } else if (KeyType.RSA.equals(signingKeyType)) {
+            keySet.setKeys(new JWK[] { JWKBuilder.create().algorithm(signingKeyAlgorithm).rsa(signingKeyPair.getPublic()) });
+        } else if (KeyType.EC.equals(signingKeyType)) {
+            keySet.setKeys(new JWK[] { JWKBuilder.create().algorithm(signingKeyAlgorithm).ec(signingKeyPair.getPublic()) });
         } else {
-            keySet.setKeys(new JWK[] { JWKBuilder.create().rs256(clientData.getSigningKeyPair().getPublic()) });
+            keySet.setKeys(new JWK[] {});
         }
 
         return keySet;
@@ -113,6 +162,7 @@ public class TestingOIDCEndpointsApplicationResource {
     public void setOIDCRequest(@QueryParam("realmName") String realmName, @QueryParam("clientId") String clientId,
                                @QueryParam("redirectUri") String redirectUri, @QueryParam("maxAge") String maxAge,
                                @QueryParam("jwaAlgorithm") String jwaAlgorithm) {
+
         Map<String, Object> oidcRequest = new HashMap<>();
         oidcRequest.put(OIDCLoginProtocol.CLIENT_ID_PARAM, clientId);
         oidcRequest.put(OIDCLoginProtocol.RESPONSE_TYPE_PARAM, OAuth2Constants.CODE);
@@ -121,20 +171,37 @@ public class TestingOIDCEndpointsApplicationResource {
             oidcRequest.put(OIDCLoginProtocol.MAX_AGE_PARAM, Integer.parseInt(maxAge));
         }
 
-        Algorithm alg = Enum.valueOf(Algorithm.class, jwaAlgorithm);
-        if (alg == Algorithm.none) {
-            clientData.setOidcRequest(new JWSBuilder().jsonContent(oidcRequest).none());
-        } else if (alg == Algorithm.RS256) {
-            if (clientData.getSigningKeyPair() == null) {
-                throw new BadRequestException("Requested RS256, but signing key not set");
-            }
+        if (!isSupportedSigningAlgorithm(jwaAlgorithm)) throw new BadRequestException("Unknown argument: " + jwaAlgorithm);
 
+        if ("none".equals(jwaAlgorithm)) {
+            clientData.setOidcRequest(new JWSBuilder().jsonContent(oidcRequest).none());
+        } else  if (clientData.getSigningKeyPair() == null) {
+            throw new BadRequestException("signing key not set");
+        } else {
             PrivateKey privateKey = clientData.getSigningKeyPair().getPrivate();
             String kid = KeyUtils.createKeyId(clientData.getSigningKeyPair().getPublic());
-            clientData.setOidcRequest(new JWSBuilder().kid(kid).jsonContent(oidcRequest).rsa256(privateKey));
-        } else {
-            throw new BadRequestException("Unknown argument: " + jwaAlgorithm);
+            KeyWrapper keyWrapper = new KeyWrapper();
+            keyWrapper.setAlgorithm(clientData.getSigningKeyAlgorithm());
+            keyWrapper.setKid(kid);
+            keyWrapper.setSignKey(privateKey);
+            SignatureSignerContext signer = new AsymmetricSignatureSignerContext(keyWrapper);
+            clientData.setOidcRequest(new JWSBuilder().kid(kid).jsonContent(oidcRequest).sign(signer));
+        }
+    }
+    
+    private boolean isSupportedSigningAlgorithm(String signingAlgorithm) {
+        boolean ret = false;
+        switch (signingAlgorithm) {
+            case "none":
+            case org.keycloak.crypto.Algorithm.RS256:
+            case org.keycloak.crypto.Algorithm.RS384:
+            case org.keycloak.crypto.Algorithm.RS512:
+            case org.keycloak.crypto.Algorithm.ES256:
+            case org.keycloak.crypto.Algorithm.ES384:
+            case org.keycloak.crypto.Algorithm.ES512:
+                ret = true;
         }
+        return ret;
     }
 
 
diff --git a/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/rest/TestApplicationResourceProviderFactory.java b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/rest/TestApplicationResourceProviderFactory.java
index d8d2a8d..8224623 100644
--- a/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/rest/TestApplicationResourceProviderFactory.java
+++ b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/rest/TestApplicationResourceProviderFactory.java
@@ -18,6 +18,8 @@
 package org.keycloak.testsuite.rest;
 
 import org.keycloak.Config.Scope;
+import org.keycloak.crypto.Algorithm;
+import org.keycloak.crypto.KeyType;
 import org.keycloak.models.KeycloakSession;
 import org.keycloak.models.KeycloakSessionFactory;
 import org.keycloak.representations.adapters.action.LogoutAction;
@@ -70,6 +72,8 @@ public class TestApplicationResourceProviderFactory implements RealmResourceProv
         private KeyPair signingKeyPair;
         private String oidcRequest;
         private List<String> sectorIdentifierRedirectUris;
+        private String signingKeyType = KeyType.RSA;
+        private String signingKeyAlgorithm = Algorithm.RS256;
 
         public KeyPair getSigningKeyPair() {
             return signingKeyPair;
@@ -94,5 +98,21 @@ public class TestApplicationResourceProviderFactory implements RealmResourceProv
         public void setSectorIdentifierRedirectUris(List<String> sectorIdentifierRedirectUris) {
             this.sectorIdentifierRedirectUris = sectorIdentifierRedirectUris;
         }
+
+        public String getSigningKeyType() {
+            return signingKeyType;
+        }
+
+        public void setSigningKeyType(String signingKeyType) {
+            this.signingKeyType = signingKeyType;
+        }
+
+        public String getSigningKeyAlgorithm() {
+            return signingKeyAlgorithm;
+        }
+
+        public void setSigningKeyAlgorithm(String signingKeyAlgorithm) {
+            this.signingKeyAlgorithm = signingKeyAlgorithm;
+        }
     }
 }
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/client/resources/TestOIDCEndpointsApplicationResource.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/client/resources/TestOIDCEndpointsApplicationResource.java
index f8d8e98..19fd5d4 100644
--- a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/client/resources/TestOIDCEndpointsApplicationResource.java
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/client/resources/TestOIDCEndpointsApplicationResource.java
@@ -35,7 +35,7 @@ public interface TestOIDCEndpointsApplicationResource {
     @GET
     @Produces(MediaType.APPLICATION_JSON)
     @Path("/generate-keys")
-    Map<String, String> generateKeys();
+    Map<String, String> generateKeys(@QueryParam("jwaAlgorithm") String jwaAlgorithm);
 
     @GET
     @Produces(MediaType.APPLICATION_JSON)
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/client/OIDCClientRegistrationTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/client/OIDCClientRegistrationTest.java
index ff3e520..a86d22d 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/client/OIDCClientRegistrationTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/client/OIDCClientRegistrationTest.java
@@ -207,17 +207,30 @@ public class OIDCClientRegistrationTest extends AbstractClientRegistrationTest {
     @Test
     public void testSignaturesRequired() throws Exception {
         OIDCClientRepresentation clientRep = createRep();
-        clientRep.setUserinfoSignedResponseAlg(Algorithm.RS256.toString());
-        clientRep.setRequestObjectSigningAlg(Algorithm.RS256.toString());
+        clientRep.setUserinfoSignedResponseAlg(Algorithm.ES256.toString());
+        clientRep.setRequestObjectSigningAlg(Algorithm.ES256.toString());
 
         OIDCClientRepresentation response = reg.oidc().create(clientRep);
-        Assert.assertEquals(Algorithm.RS256.toString(), response.getUserinfoSignedResponseAlg());
-        Assert.assertEquals(Algorithm.RS256.toString(), response.getRequestObjectSigningAlg());
+        Assert.assertEquals(Algorithm.ES256.toString(), response.getUserinfoSignedResponseAlg());
+        Assert.assertEquals(Algorithm.ES256.toString(), response.getRequestObjectSigningAlg());
         Assert.assertNotNull(response.getClientSecret());
 
         // Test Keycloak representation
         ClientRepresentation kcClient = getClient(response.getClientId());
         OIDCAdvancedConfigWrapper config = OIDCAdvancedConfigWrapper.fromClientRepresentation(kcClient);
+        Assert.assertEquals(config.getUserInfoSignedResponseAlg(), Algorithm.ES256);
+        Assert.assertEquals(config.getRequestObjectSignatureAlg(), Algorithm.ES256);
+
+        // update (ES256 to RS256)
+        clientRep.setUserinfoSignedResponseAlg(Algorithm.RS256.toString());
+        clientRep.setRequestObjectSigningAlg(Algorithm.RS256.toString());
+        response = reg.oidc().create(clientRep);
+        Assert.assertEquals(Algorithm.RS256.toString(), response.getUserinfoSignedResponseAlg());
+        Assert.assertEquals(Algorithm.RS256.toString(), response.getRequestObjectSigningAlg());
+
+        // keycloak representation
+        kcClient = getClient(response.getClientId());
+        config = OIDCAdvancedConfigWrapper.fromClientRepresentation(kcClient);
         Assert.assertEquals(config.getUserInfoSignedResponseAlg(), Algorithm.RS256);
         Assert.assertEquals(config.getRequestObjectSignatureAlg(), Algorithm.RS256);
     }
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/client/OIDCJwksClientRegistrationTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/client/OIDCJwksClientRegistrationTest.java
index fe2e886..0c8dd0a 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/client/OIDCJwksClientRegistrationTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/client/OIDCJwksClientRegistrationTest.java
@@ -28,7 +28,6 @@ import java.util.Map;
 
 import javax.ws.rs.core.UriBuilder;
 
-import org.apache.http.HttpResponse;
 import org.apache.http.NameValuePair;
 import org.apache.http.client.entity.UrlEncodedFormEntity;
 import org.apache.http.client.methods.CloseableHttpResponse;
@@ -49,7 +48,6 @@ import org.keycloak.jose.jwk.JWK;
 import org.keycloak.jose.jwk.JWKBuilder;
 import org.keycloak.jose.jws.JWSBuilder;
 import org.keycloak.keys.PublicKeyStorageUtils;
-import org.keycloak.keys.loader.PublicKeyStorageManager;
 import org.keycloak.models.utils.KeycloakModelUtils;
 import org.keycloak.protocol.oidc.OIDCLoginProtocol;
 import org.keycloak.protocol.oidc.OIDCLoginProtocolService;
@@ -106,7 +104,7 @@ public class OIDCJwksClientRegistrationTest extends AbstractClientRegistrationTe
 
         // Generate keys for client
         TestOIDCEndpointsApplicationResource oidcClientEndpointsResource = testingClient.testApp().oidcClientEndpoints();
-        Map<String, String> generatedKeys = oidcClientEndpointsResource.generateKeys();
+        Map<String, String> generatedKeys = oidcClientEndpointsResource.generateKeys("RS256");
 
         JSONWebKeySet keySet = oidcClientEndpointsResource.getJwks();
         clientRep.setJwks(keySet);
@@ -131,7 +129,7 @@ public class OIDCJwksClientRegistrationTest extends AbstractClientRegistrationTe
 
         // Generate keys for client
         TestOIDCEndpointsApplicationResource oidcClientEndpointsResource = testingClient.testApp().oidcClientEndpoints();
-        Map<String, String> generatedKeys = oidcClientEndpointsResource.generateKeys();
+        Map<String, String> generatedKeys = oidcClientEndpointsResource.generateKeys("RS256");
 
         JSONWebKeySet keySet = oidcClientEndpointsResource.getJwks();
         clientRep.setJwks(keySet);
@@ -163,7 +161,7 @@ public class OIDCJwksClientRegistrationTest extends AbstractClientRegistrationTe
 
         // Generate keys for client
         TestOIDCEndpointsApplicationResource oidcClientEndpointsResource = testingClient.testApp().oidcClientEndpoints();
-        oidcClientEndpointsResource.generateKeys();
+        oidcClientEndpointsResource.generateKeys("RS256");
 
         JSONWebKeySet keySet = oidcClientEndpointsResource.getJwks();
 
@@ -250,7 +248,7 @@ public class OIDCJwksClientRegistrationTest extends AbstractClientRegistrationTe
 
         // Generate keys for client
         TestOIDCEndpointsApplicationResource oidcClientEndpointsResource = testingClient.testApp().oidcClientEndpoints();
-        Map<String, String> generatedKeys = oidcClientEndpointsResource.generateKeys();
+        Map<String, String> generatedKeys = oidcClientEndpointsResource.generateKeys("RS256");
 
         clientRep.setJwksUri(TestApplicationResourceUrls.clientJwksUri());
 
@@ -273,7 +271,7 @@ public class OIDCJwksClientRegistrationTest extends AbstractClientRegistrationTe
 
         // Generate keys for client
         TestOIDCEndpointsApplicationResource oidcClientEndpointsResource = testingClient.testApp().oidcClientEndpoints();
-        Map<String, String> generatedKeys = oidcClientEndpointsResource.generateKeys();
+        Map<String, String> generatedKeys = oidcClientEndpointsResource.generateKeys("RS256");
 
         clientRep.setJwksUri(TestApplicationResourceUrls.clientJwksUri());
 
@@ -287,7 +285,7 @@ public class OIDCJwksClientRegistrationTest extends AbstractClientRegistrationTe
         assertAuthenticateClientSuccess(generatedKeys, response, KEEP_GENERATED_KID);
 
         // Add new key to the jwks
-        Map<String, String> generatedKeys2 = oidcClientEndpointsResource.generateKeys();
+        Map<String, String> generatedKeys2 = oidcClientEndpointsResource.generateKeys("RS256");
 
         // Error should happen. KeyStorageProvider won't yet download new keys because of timeout
         assertAuthenticateClientError(generatedKeys2, response, KEEP_GENERATED_KID);
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oidc/OIDCAdvancedRequestParamsTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oidc/OIDCAdvancedRequestParamsTest.java
index 80fbe54..01859b0 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oidc/OIDCAdvancedRequestParamsTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oidc/OIDCAdvancedRequestParamsTest.java
@@ -796,7 +796,7 @@ public class OIDCAdvancedRequestParamsTest extends AbstractTestRealmKeycloakTest
         assertEquals("Invalid Request", errorPage.getError());
 
         // Generate keypair for client
-        String clientPublicKeyPem = oidcClientEndpointsResource.generateKeys().get(TestingOIDCEndpointsApplicationResource.PUBLIC_KEY);
+        String clientPublicKeyPem = oidcClientEndpointsResource.generateKeys(null).get(TestingOIDCEndpointsApplicationResource.PUBLIC_KEY);
 
         // Verify signed request_uri will fail due to failed signature validation
         oidcClientEndpointsResource.setOIDCRequest("test", "test-app", validRedirectUri, "10", Algorithm.RS256.toString());
@@ -826,6 +826,117 @@ public class OIDCAdvancedRequestParamsTest extends AbstractTestRealmKeycloakTest
         clientResource.update(clientRep);
     }
 
+    private void requestUriParamSignedIn(Algorithm expectedAlgorithm, Algorithm actualAlgorithm) throws Exception {
+        ClientResource clientResource = null;
+        ClientRepresentation clientRep = null;
+        try {
+            oauth.stateParamHardcoded("mystate3");
+
+            String validRedirectUri = oauth.getRedirectUri();
+            TestOIDCEndpointsApplicationResource oidcClientEndpointsResource = testingClient.testApp().oidcClientEndpoints();
+
+            // Set required signature for request_uri
+            clientResource = ApiUtil.findClientByClientId(adminClient.realm("test"), "test-app");
+            clientRep = clientResource.toRepresentation();
+            OIDCAdvancedConfigWrapper.fromClientRepresentation(clientRep).setRequestObjectSignatureAlg(expectedAlgorithm);
+            clientResource.update(clientRep);
+
+            // generate and register client keypair
+            if (Algorithm.none != actualAlgorithm) oidcClientEndpointsResource.generateKeys(actualAlgorithm.name());
+
+            // register request object
+            oidcClientEndpointsResource.setOIDCRequest("test", "test-app", validRedirectUri, "10", actualAlgorithm.name());
+
+            // use and set jwks_url
+            clientResource = ApiUtil.findClientByClientId(adminClient.realm("test"), "test-app");
+            clientRep = clientResource.toRepresentation();
+            OIDCAdvancedConfigWrapper.fromClientRepresentation(clientRep).setUseJwksUrl(true);
+            String jwksUrl = TestApplicationResourceUrls.clientJwksUri();
+            OIDCAdvancedConfigWrapper.fromClientRepresentation(clientRep).setJwksUrl(jwksUrl);
+            clientResource.update(clientRep);
+
+            // set time offset, so that new keys are downloaded
+            setTimeOffset(20);
+
+            oauth.requestUri(TestApplicationResourceUrls.clientRequestUri());
+            if (expectedAlgorithm == null || expectedAlgorithm == actualAlgorithm) {
+                // Check signed request_uri will pass
+                OAuthClient.AuthorizationEndpointResponse response = oauth.doLogin("test-user@localhost", "password");
+                Assert.assertNotNull(response.getCode());
+                Assert.assertEquals("mystate3", response.getState());
+                assertTrue(appPage.isCurrent());
+            } else {
+                // Verify signed request_uri will fail due to failed signature validation
+                oauth.openLoginForm();
+                Assert.assertTrue(errorPage.isCurrent());
+                assertEquals("Invalid Request", errorPage.getError());
+            }
+
+        } finally {
+            // Revert requiring signature for client
+            OIDCAdvancedConfigWrapper.fromClientRepresentation(clientRep).setRequestObjectSignatureAlg(null);
+            // Revert jwks_url settings
+            OIDCAdvancedConfigWrapper.fromClientRepresentation(clientRep).setUseJwksUrl(false);
+            OIDCAdvancedConfigWrapper.fromClientRepresentation(clientRep).setJwksUrl(null);
+            clientResource.update(clientRep);
+        }
+    }
+
+    @Test
+    public void requestUriParamSignedExpectedES256ActualRS256() throws Exception {
+        // will fail
+        requestUriParamSignedIn(Algorithm.ES256, Algorithm.RS256);
+    }
+
+    @Test
+    public void requestUriParamSignedExpectedNoneActualES256() throws Exception {
+        // will fail
+        requestUriParamSignedIn(Algorithm.none, Algorithm.ES256);
+    }
+
+    @Test
+    public void requestUriParamSignedExpectedNoneActualNone() throws Exception {
+        // will success
+        requestUriParamSignedIn(Algorithm.none, Algorithm.none);
+    }
+
+    @Test
+    public void requestUriParamSignedExpectedES256ActualES256() throws Exception {
+        // will success
+        requestUriParamSignedIn(Algorithm.ES256, Algorithm.ES256);
+    }
+
+    @Test
+    public void requestUriParamSignedExpectedES384ActualES384() throws Exception {
+        // will success
+        requestUriParamSignedIn(Algorithm.ES384, Algorithm.ES384);
+    }
+
+    @Test
+    public void requestUriParamSignedExpectedES512ActualES512() throws Exception {
+        // will success
+        requestUriParamSignedIn(Algorithm.ES512, Algorithm.ES512);
+    }
+
+    @Test
+    public void requestUriParamSignedExpectedRS384ActualRS384() throws Exception {
+        // will success
+        requestUriParamSignedIn(Algorithm.RS384, Algorithm.RS384);
+    }
+
+    @Test
+    public void requestUriParamSignedExpectedRS512ActualRS512() throws Exception {
+        // will success
+        requestUriParamSignedIn(Algorithm.RS512, Algorithm.RS512);
+    }
+
+    @Test
+    public void requestUriParamSignedExpectedAnyActualES256() throws Exception {
+        // Algorithm is null if 'any'
+        // will success
+        requestUriParamSignedIn(null, Algorithm.ES256);
+    }
+
     // LOGIN_HINT
 
     @Test
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oidc/OIDCWellKnownProviderTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oidc/OIDCWellKnownProviderTest.java
index 26a740e..2da4622 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oidc/OIDCWellKnownProviderTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oidc/OIDCWellKnownProviderTest.java
@@ -97,7 +97,6 @@ public class OIDCWellKnownProviderTest extends AbstractKeycloakTest {
         oauth.clientId("test-app");
     }
 
-
     @Test
     public void testDiscovery() {
         Client client = ClientBuilder.newClient();
@@ -127,7 +126,7 @@ public class OIDCWellKnownProviderTest extends AbstractKeycloakTest {
             Assert.assertNames(oidcConfig.getSubjectTypesSupported(), "pairwise", "public");
             Assert.assertNames(oidcConfig.getIdTokenSigningAlgValuesSupported(), Algorithm.RS256, Algorithm.RS384, Algorithm.RS512, Algorithm.ES256, Algorithm.ES384, Algorithm.ES512, Algorithm.HS256, Algorithm.HS384, Algorithm.HS512);
             Assert.assertNames(oidcConfig.getUserInfoSigningAlgValuesSupported(), "none", Algorithm.RS256, Algorithm.RS384, Algorithm.RS512, Algorithm.ES256, Algorithm.ES384, Algorithm.ES512, Algorithm.HS256, Algorithm.HS384, Algorithm.HS512);
-            Assert.assertNames(oidcConfig.getRequestObjectSigningAlgValuesSupported(), "none", Algorithm.RS256);
+            Assert.assertNames(oidcConfig.getRequestObjectSigningAlgValuesSupported(), "none", Algorithm.RS256, Algorithm.RS384, Algorithm.RS512, Algorithm.ES256, Algorithm.ES384, Algorithm.ES512);
 
             // Client authentication
             Assert.assertNames(oidcConfig.getTokenEndpointAuthMethodsSupported(), "client_secret_basic", "client_secret_post", "private_key_jwt", "client_secret_jwt");
diff --git a/themes/src/main/resources/theme/base/admin/resources/js/controllers/clients.js b/themes/src/main/resources/theme/base/admin/resources/js/controllers/clients.js
index cabe47a..e86371b 100755
--- a/themes/src/main/resources/theme/base/admin/resources/js/controllers/clients.js
+++ b/themes/src/main/resources/theme/base/admin/resources/js/controllers/clients.js
@@ -959,12 +959,6 @@ module.controller('ClientDetailCtrl', function($scope, realm, client, flows, $ro
         {name: "INCLUSIVE_WITH_COMMENTS", value: "http://www.w3.org/TR/2001/REC-xml-c14n-20010315#WithComments"}
     ];
 
-    $scope.requestObjectSignatureAlgorithms = [
-        "any",
-        "none",
-        "RS256"
-    ];
-    
     $scope.requestObjectRequiredOptions = [
         "not required",
         "request or request_uri",
diff --git a/themes/src/main/resources/theme/base/admin/resources/partials/client-detail.html b/themes/src/main/resources/theme/base/admin/resources/partials/client-detail.html
index 3884671..ca540be 100755
--- a/themes/src/main/resources/theme/base/admin/resources/partials/client-detail.html
+++ b/themes/src/main/resources/theme/base/admin/resources/partials/client-detail.html
@@ -438,8 +438,10 @@
                     <div>
                         <select class="form-control" id="requestObjectSignatureAlg"
                                 ng-change="changeRequestObjectSignatureAlg()"
-                                ng-model="requestObjectSignatureAlg"
-                                ng-options="sig for sig in requestObjectSignatureAlgorithms">
+                                ng-model="requestObjectSignatureAlg">
+                            <option value="any">any</option>
+                            <option value="none">none</option>
+                            <option ng-repeat="provider in serverInfo.listProviderIds('clientSignature')" value="{{provider}}">{{provider}}</option>
                         </select>
                     </div>
                 </div>