KEYCLOAK-14529 Signed and Encrypted ID Token Support : RSA-OAEP-256 Key Management Algorithm

This commit is contained in:
Dillon Sellars 2020-06-13 19:12:40 -04:00 committed by Marek Posolda
parent 7f979ffbcf
commit 25bb2e3ba2
9 changed files with 144 additions and 3 deletions

View file

@ -26,6 +26,7 @@ public class JWEConstants {
public static final String A128KW = "A128KW";
public static final String RSA1_5 = "RSA1_5";
public static final String RSA_OAEP = "RSA-OAEP";
public static final String RSA_OAEP_256 = "RSA-OAEP-256";
public static final String A128CBC_HS256 = "A128CBC-HS256";
public static final String A192CBC_HS384 = "A192CBC-HS384";

View file

@ -0,0 +1,67 @@
/*
* Copyright 2018 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.jose.jwe.alg;
import org.keycloak.jose.jwe.JWEKeyStorage;
import org.keycloak.jose.jwe.enc.JWEEncryptionProvider;
import javax.crypto.Cipher;
import javax.crypto.spec.OAEPParameterSpec;
import javax.crypto.spec.PSource;
import java.security.AlgorithmParameters;
import java.security.Key;
import java.security.spec.AlgorithmParameterSpec;
import java.security.spec.MGF1ParameterSpec;
public class RsaKeyEncryption256JWEAlgorithmProvider extends KeyEncryptionJWEAlgorithmProvider {
private final String jcaAlgorithmName;
public RsaKeyEncryption256JWEAlgorithmProvider(String jcaAlgorithmName) {
this.jcaAlgorithmName = jcaAlgorithmName;
}
@Override
protected Cipher getCipherProvider() throws Exception {
return Cipher.getInstance(jcaAlgorithmName);
}
@Override
public byte[] decodeCek(byte[] encodedCek, Key privateKey) throws Exception {
AlgorithmParameters algp = AlgorithmParameters.getInstance("OAEP");
AlgorithmParameterSpec paramSpec = new OAEPParameterSpec("SHA-256", "MGF1", MGF1ParameterSpec.SHA256,
PSource.PSpecified.DEFAULT);
algp.init(paramSpec);
Cipher cipher = getCipherProvider();
cipher.init(Cipher.DECRYPT_MODE, privateKey, algp);
return cipher.doFinal(encodedCek);
}
@Override
public byte[] encodeCek(JWEEncryptionProvider encryptionProvider, JWEKeyStorage keyStorage, Key publicKey)
throws Exception {
AlgorithmParameters algp = AlgorithmParameters.getInstance("OAEP");
AlgorithmParameterSpec paramSpec = new OAEPParameterSpec("SHA-256", "MGF1", MGF1ParameterSpec.SHA256,
PSource.PSpecified.DEFAULT);
algp.init(paramSpec);
Cipher cipher = getCipherProvider();
cipher.init(Cipher.ENCRYPT_MODE, publicKey, algp);
byte[] cekBytes = keyStorage.getCekBytes();
return cipher.doFinal(cekBytes);
}
}

View file

@ -251,6 +251,11 @@ public class JWETest {
testKeyEncryption_ContentEncryptionAesGcm(JWEConstants.RSA_OAEP, JWEConstants.A128GCM);
}
@Test
public void testRSAOAEP256_A128GCM() throws Exception {
testKeyEncryption_ContentEncryptionAesGcm(JWEConstants.RSA_OAEP_256, JWEConstants.A128GCM);
}
@Test
public void testRSA1_5_A128CBCHS256() throws Exception {
testKeyEncryption_ContentEncryptionAesHmacSha(JWEConstants.RSA1_5, JWEConstants.A128CBC_HS256);
@ -260,6 +265,11 @@ public class JWETest {
public void testRSAOAEP_A128CBCHS256() throws Exception {
testKeyEncryption_ContentEncryptionAesHmacSha(JWEConstants.RSA_OAEP, JWEConstants.A128CBC_HS256);
}
@Test
public void testRSAOAEP256_A128CBCHS256() throws Exception {
testKeyEncryption_ContentEncryptionAesHmacSha(JWEConstants.RSA_OAEP_256, JWEConstants.A128CBC_HS256);
}
private void testKeyEncryption_ContentEncryptionAesGcm(String jweAlgorithmName, String jweEncryptionName) throws Exception {
// generate key pair for KEK
@ -336,6 +346,8 @@ public class JWETest {
jcaAlgorithmName = "RSA/ECB/PKCS1Padding";
} else if (JWEConstants.RSA_OAEP.equals(jweAlgorithmName)) {
jcaAlgorithmName = "RSA/ECB/OAEPWithSHA-1AndMGF1Padding";
} else if (JWEConstants.RSA_OAEP_256.equals(jweAlgorithmName)) {
jcaAlgorithmName = "RSA/ECB/OAEPWithSHA-256AndMGF1Padding";
}
return jcaAlgorithmName;
}

View file

@ -19,6 +19,7 @@ package org.keycloak.crypto;
import org.keycloak.jose.jwe.JWEConstants;
import org.keycloak.jose.jwe.alg.JWEAlgorithmProvider;
import org.keycloak.jose.jwe.alg.RsaKeyEncryption256JWEAlgorithmProvider;
import org.keycloak.jose.jwe.alg.RsaKeyEncryptionJWEAlgorithmProvider;
import org.keycloak.models.KeycloakSession;
@ -39,6 +40,8 @@ public class RsaCekManagementProvider implements CekManagementProvider {
jcaAlgorithmName = "RSA/ECB/PKCS1Padding";
} else if (JWEConstants.RSA_OAEP.equals(jweAlgorithmName)) {
jcaAlgorithmName = "RSA/ECB/OAEPWithSHA-1AndMGF1Padding";
} else if (JWEConstants.RSA_OAEP_256.equals(jweAlgorithmName)) {
return new RsaKeyEncryption256JWEAlgorithmProvider("RSA/ECB/OAEPWithSHA-256AndMGF1Padding");
}
return new RsaKeyEncryptionJWEAlgorithmProvider(jcaAlgorithmName);
}

View file

@ -0,0 +1,37 @@
/*
* Copyright 2018 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.jose.jwe.JWEConstants;
import org.keycloak.models.KeycloakSession;
public class RsaesOaep256CekManagementProviderFactory implements CekManagementProviderFactory {
public static final String ID = JWEConstants.RSA_OAEP_256;
@Override
public String getId() {
return ID;
}
@Override
public CekManagementProvider create(KeycloakSession session) {
return new RsaCekManagementProvider(session, ID);
}
}

View file

@ -1,2 +1,3 @@
org.keycloak.crypto.RsaesPkcs1CekManagementProviderFactory
org.keycloak.crypto.RsaesOaepCekManagementProviderFactory
org.keycloak.crypto.RsaesOaepCekManagementProviderFactory
org.keycloak.crypto.RsaesOaep256CekManagementProviderFactory

View file

@ -105,6 +105,7 @@ public class TestingOIDCEndpointsApplicationResource {
break;
case JWEConstants.RSA1_5:
case JWEConstants.RSA_OAEP:
case JWEConstants.RSA_OAEP_256:
// for JWE KEK Key Encryption
keyType = KeyType.RSA;
keyUse = KeyUse.ENC;
@ -245,6 +246,7 @@ public class TestingOIDCEndpointsApplicationResource {
case Algorithm.ES512:
case JWEConstants.RSA1_5:
case JWEConstants.RSA_OAEP:
case JWEConstants.RSA_OAEP_256:
ret = true;
}
return ret;

View file

@ -151,6 +151,23 @@ public class IdTokenEncryptionTest extends AbstractTestRealmKeycloakTest {
testIdTokenSignatureAndEncryption(Algorithm.PS512, JWEConstants.RSA_OAEP, JWEConstants.A256CBC_HS512);
}
@Test
public void testIdTokenEncryptionAlgRSA_OAEP256EncA128CBC_HS256() {
// add key provider explicitly though DefaultKeyManager create fallback key provider if not exist
TokenSignatureUtil.registerKeyProvider("P-521", adminClient, testContext);
testIdTokenSignatureAndEncryption(Algorithm.ES512, JWEConstants.RSA_OAEP_256, JWEConstants.A128CBC_HS256);
}
@Test
public void testIdTokenEncryptionAlgRSA_OAEP256EncA192CBC_HS384() {
testIdTokenSignatureAndEncryption(Algorithm.PS256, JWEConstants.RSA_OAEP_256, JWEConstants.A192CBC_HS384);
}
@Test
public void testIdTokenEncryptionAlgRSA_OAEP256EncA256CBC_HS512() {
testIdTokenSignatureAndEncryption(Algorithm.PS512, JWEConstants.RSA_OAEP_256, JWEConstants.A256CBC_HS512);
}
@Test
public void testIdTokenEncryptionAlgRSA_OAEPEncA128GCM() {
// add key provider explicitly though DefaultKeyManager create fallback key provider if not exist
@ -231,7 +248,8 @@ public class IdTokenEncryptionTest extends AbstractTestRealmKeycloakTest {
private JWEAlgorithmProvider getJweAlgorithmProvider(String algAlgorithm) {
JWEAlgorithmProvider jweAlgorithmProvider = null;
if (JWEConstants.RSA1_5.equals(algAlgorithm) || JWEConstants.RSA_OAEP.equals(algAlgorithm) ) {
if (JWEConstants.RSA1_5.equals(algAlgorithm) || JWEConstants.RSA_OAEP.equals(algAlgorithm) ||
JWEConstants.RSA_OAEP_256.equals(algAlgorithm)) {
jweAlgorithmProvider = new RsaCekManagementProvider(null, algAlgorithm).jweAlgorithmProvider();
}
return jweAlgorithmProvider;

View file

@ -133,7 +133,7 @@ public class OIDCWellKnownProviderTest extends AbstractKeycloakTest {
Assert.assertNames(oidcConfig.getRequestObjectSigningAlgValuesSupported(), "none", Algorithm.PS256, Algorithm.PS384, Algorithm.PS512, Algorithm.RS256, Algorithm.RS384, Algorithm.RS512, Algorithm.ES256, Algorithm.ES384, Algorithm.ES512, Algorithm.HS256, Algorithm.HS384, Algorithm.HS512);
// Encryption algorithms
Assert.assertNames(oidcConfig.getIdTokenEncryptionAlgValuesSupported(), JWEConstants.RSA1_5, JWEConstants.RSA_OAEP);
Assert.assertNames(oidcConfig.getIdTokenEncryptionAlgValuesSupported(), JWEConstants.RSA1_5, JWEConstants.RSA_OAEP, JWEConstants.RSA_OAEP_256);
Assert.assertNames(oidcConfig.getIdTokenEncryptionEncValuesSupported(), JWEConstants.A128CBC_HS256, JWEConstants.A128GCM, JWEConstants.A192CBC_HS384, JWEConstants.A192GCM, JWEConstants.A256CBC_HS512, JWEConstants.A256GCM);
// Client authentication