KEYCLOAK-14529 Signed and Encrypted ID Token Support : RSA-OAEP-256 Key Management Algorithm
This commit is contained in:
parent
7f979ffbcf
commit
25bb2e3ba2
9 changed files with 144 additions and 3 deletions
|
@ -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";
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -1,2 +1,3 @@
|
|||
org.keycloak.crypto.RsaesPkcs1CekManagementProviderFactory
|
||||
org.keycloak.crypto.RsaesOaepCekManagementProviderFactory
|
||||
org.keycloak.crypto.RsaesOaepCekManagementProviderFactory
|
||||
org.keycloak.crypto.RsaesOaep256CekManagementProviderFactory
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue