[KEYCLOAK-12299] JWKS parsing: fallback to RS256 for RSA keys without alg field

This commit is contained in:
Tom Billiet 2020-01-06 14:57:12 +01:00 committed by Stian Thorgersen
parent 062cbf4e0a
commit 0f8d988d58
2 changed files with 125 additions and 1 deletions

View file

@ -52,7 +52,13 @@ public class JWKSUtils {
if (jwk.getPublicKeyUse().equals(requestedUse.asString()) && parser.isKeyTypeSupported(jwk.getKeyType())) { if (jwk.getPublicKeyUse().equals(requestedUse.asString()) && parser.isKeyTypeSupported(jwk.getKeyType())) {
KeyWrapper keyWrapper = new KeyWrapper(); KeyWrapper keyWrapper = new KeyWrapper();
keyWrapper.setKid(jwk.getKeyId()); keyWrapper.setKid(jwk.getKeyId());
keyWrapper.setAlgorithm(jwk.getAlgorithm()); if (jwk.getAlgorithm() != null) {
keyWrapper.setAlgorithm(jwk.getAlgorithm());
}
else if (jwk.getKeyType().equalsIgnoreCase("RSA")){
//backwards compatibility: RSA keys without "alg" field set are considered RS256
keyWrapper.setAlgorithm("RS256");
}
keyWrapper.setType(jwk.getKeyType()); keyWrapper.setType(jwk.getKeyType());
keyWrapper.setUse(getKeyUse(jwk.getPublicKeyUse())); keyWrapper.setUse(getKeyUse(jwk.getPublicKeyUse()));
keyWrapper.setPublicKey(parser.toPublicKey()); keyWrapper.setPublicKey(parser.toPublicKey());

View file

@ -0,0 +1,118 @@
/*
* 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.util;
import org.junit.Test;
import org.keycloak.common.util.Base64Url;
import org.keycloak.common.util.CertificateUtils;
import org.keycloak.common.util.KeyUtils;
import org.keycloak.common.util.PemUtils;
import org.keycloak.crypto.JavaAlgorithm;
import org.keycloak.crypto.KeyUse;
import org.keycloak.crypto.KeyWrapper;
import org.keycloak.jose.jwk.*;
import java.nio.charset.StandardCharsets;
import java.security.*;
import java.security.cert.X509Certificate;
import java.security.spec.ECGenParameterSpec;
import java.util.Map;
import static org.junit.Assert.*;
public class JWKSUtilsTest {
@Test
public void publicRs256() throws Exception {
Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
String kidRsa1 = "key1";
String kidRsa2 = "key2";
String kidEC1 = "key3";
String kidEC2 = "key4";
String jwksJson = "{" +
"\"keys\": [" +
" {" +
" \"kty\": \"RSA\"," +
" \"alg\": \"RS256\"," +
" \"use\": \"sig\"," +
" \"kid\": \"" + kidRsa1 + "\"," +
" \"n\": \"soFDjoZ5mQ8XAA7reQAFg90inKAHk0DXMTizo4JuOsgzUbhcplIeZ7ks83hsEjm8mP8lUVaHMPMAHEIp3gu6Xxsg-s73ofx1dtt_Fo7aj8j383MFQGl8-FvixTVobNeGeC0XBBQjN8lEl-lIwOa4ZoERNAShplTej0ntDp7TQm0=\"," +
" \"e\": \"AQAB\"" +
" }" +
" ,{" +
" \"kty\": \"RSA\"," +
" \"use\": \"sig\"," +
" \"kid\": \"" + kidRsa2 + "\"," +
" \"n\": \"soFDjoZ5mQ8XAA7reQAFg90inKAHk0DXMTizo4JuOsgzUbhcplIeZ7ks83hsEjm8mP8lUVaHMPMAHEIp3gu6Xxsg-s73ofx1dtt_Fo7aj8j383MFQGl8-FvixTVobNeGeC0XBBQjN8lEl-lIwOa4ZoERNAShplTej0ntDp7TQm0=\"," +
" \"e\": \"AQAB\"" +
" }," +
" {" +
" \"kty\": \"EC\"," +
" \"use\": \"sig\"," +
" \"crv\": \"P-384\"," +
" \"kid\": \"" + kidEC1 + "\"," +
" \"x\": \"KVZ5h_W0-8fXmUrxmyRpO_9vwwI7urXfyxGdxm1hpEuhPj2hhDxivnb2BhNvtC6O\"," +
" \"y\": \"1J3JVw_zR3uB3biAE7fs3V_4tJy2M1JinzWj9a4je5GSoW6zgGV4bk85OcuyUAhj\"," +
" \"alg\": \"ES384\"" +
" }," +
" {" +
" \"kty\": \"EC\"," +
" \"use\": \"sig\"," +
" \"crv\": \"P-384\"," +
" \"kid\": \"" + kidEC2 + "\"," +
" \"x\": \"KVZ5h_W0-8fXmUrxmyRpO_9vwwI7urXfyxGdxm1hpEuhPj2hhDxivnb2BhNvtC6O\"," +
" \"y\": \"1J3JVw_zR3uB3biAE7fs3V_4tJy2M1JinzWj9a4je5GSoW6zgGV4bk85OcuyUAhj\"" +
" }" +
"] }";
JSONWebKeySet jsonWebKeySet = JsonSerialization.readValue(jwksJson, JSONWebKeySet.class);
Map<String, KeyWrapper> keyWrappersForUse = JWKSUtils.getKeyWrappersForUse(jsonWebKeySet, JWK.Use.SIG);
assertEquals(4, keyWrappersForUse.size());
KeyWrapper key = keyWrappersForUse.get(kidRsa1);
assertNotNull(key);
assertEquals("RS256", key.getAlgorithm());
assertEquals(KeyUse.SIG, key.getUse());
assertEquals(kidRsa1, key.getKid());
assertEquals("RSA", key.getType());
key = keyWrappersForUse.get(kidRsa2);
assertNotNull(key);
assertEquals("RS256", key.getAlgorithm());
assertEquals(KeyUse.SIG, key.getUse());
assertEquals(kidRsa2, key.getKid());
assertEquals("RSA", key.getType());
key = keyWrappersForUse.get(kidEC1);
assertNotNull(key);
assertEquals("ES384", key.getAlgorithm());
assertEquals(KeyUse.SIG, key.getUse());
assertEquals(kidEC1, key.getKid());
assertEquals("EC", key.getType());
key = keyWrappersForUse.get(kidEC2);
assertNotNull(key);
assertNull(key.getAlgorithm());
assertEquals(KeyUse.SIG, key.getUse());
assertEquals(kidEC2, key.getKid());
assertEquals("EC", key.getType());
}
}