From c057b994e7dc9afe5d639bf184ae625f88658d55 Mon Sep 17 00:00:00 2001 From: Takashi Norimatsu Date: Sun, 1 Mar 2020 21:01:46 +0900 Subject: [PATCH] KEYCLOAK-13104 Signed and Encrypted ID Token Support : AES 192bit and 256bit key support --- .../org/keycloak/jose/jwe/JWEConstants.java | 5 +- .../AesCbcHmacShaJWEEncryptionProvider.java | 10 ++++ .../jwe/enc/AesGcmJWEEncryptionProvider.java | 6 ++ ...ha384ContentEncryptionProviderFactory.java | 36 ++++++++++++ ...92GcmContentEncryptionProviderFactory.java | 36 ++++++++++++ ...ha512ContentEncryptionProviderFactory.java | 36 ++++++++++++ ...56GcmContentEncryptionProviderFactory.java | 36 ++++++++++++ ...ak.crypto.ContentEncryptionProviderFactory | 6 +- .../testsuite/oidc/IdTokenEncryptionTest.java | 55 +++++++++++++++++-- .../oidc/OIDCWellKnownProviderTest.java | 2 +- 10 files changed, 221 insertions(+), 7 deletions(-) create mode 100644 services/src/main/java/org/keycloak/crypto/Aes192CbcHmacSha384ContentEncryptionProviderFactory.java create mode 100644 services/src/main/java/org/keycloak/crypto/Aes192GcmContentEncryptionProviderFactory.java create mode 100644 services/src/main/java/org/keycloak/crypto/Aes256CbcHmacSha512ContentEncryptionProviderFactory.java create mode 100644 services/src/main/java/org/keycloak/crypto/Aes256GcmContentEncryptionProviderFactory.java diff --git a/core/src/main/java/org/keycloak/jose/jwe/JWEConstants.java b/core/src/main/java/org/keycloak/jose/jwe/JWEConstants.java index 0a05a5666c..6347e779b5 100644 --- a/core/src/main/java/org/keycloak/jose/jwe/JWEConstants.java +++ b/core/src/main/java/org/keycloak/jose/jwe/JWEConstants.java @@ -29,6 +29,9 @@ public class JWEConstants { public static final String A128CBC_HS256 = "A128CBC-HS256"; public static final String A192CBC_HS384 = "A192CBC-HS384"; - public static final String A256CBC_HS512 = "A256CBC-HS512"; + public static final String A256CBC_HS512 = "A256CBC-HS512"; public static final String A128GCM = "A128GCM"; + public static final String A192GCM = "A192GCM"; + public static final String A256GCM = "A256GCM"; + } diff --git a/core/src/main/java/org/keycloak/jose/jwe/enc/AesCbcHmacShaJWEEncryptionProvider.java b/core/src/main/java/org/keycloak/jose/jwe/enc/AesCbcHmacShaJWEEncryptionProvider.java index 3edb08e142..c498a71dd1 100644 --- a/core/src/main/java/org/keycloak/jose/jwe/enc/AesCbcHmacShaJWEEncryptionProvider.java +++ b/core/src/main/java/org/keycloak/jose/jwe/enc/AesCbcHmacShaJWEEncryptionProvider.java @@ -33,6 +33,16 @@ public class AesCbcHmacShaJWEEncryptionProvider extends AesCbcHmacShaEncryptionP expectedAesKeyLength = 16; hmacShaAlgorithm = "HMACSHA256"; authenticationTagLength = 16; + } else if (JWEConstants.A192CBC_HS384.equals(jwaAlgorithmName)) { + expectedCEKLength = 48; + expectedAesKeyLength = 24; + hmacShaAlgorithm = "HMACSHA384"; + authenticationTagLength = 24; + } else if (JWEConstants.A256CBC_HS512.equals(jwaAlgorithmName)) { + expectedCEKLength = 64; + expectedAesKeyLength = 32; + hmacShaAlgorithm = "HMACSHA512"; + authenticationTagLength = 32; } else { expectedCEKLength = 0; expectedAesKeyLength = 0; diff --git a/core/src/main/java/org/keycloak/jose/jwe/enc/AesGcmJWEEncryptionProvider.java b/core/src/main/java/org/keycloak/jose/jwe/enc/AesGcmJWEEncryptionProvider.java index ba5cea79f4..f962591ff0 100644 --- a/core/src/main/java/org/keycloak/jose/jwe/enc/AesGcmJWEEncryptionProvider.java +++ b/core/src/main/java/org/keycloak/jose/jwe/enc/AesGcmJWEEncryptionProvider.java @@ -29,6 +29,12 @@ public class AesGcmJWEEncryptionProvider extends AesGcmEncryptionProvider { if (JWEConstants.A128GCM.equals(jwaAlgorithmName)) { expectedAesKeyLength = 16; expectedCEKLength = 16; + } else if (JWEConstants.A192GCM.equals(jwaAlgorithmName)) { + expectedAesKeyLength = 24; + expectedCEKLength = 24; + } else if (JWEConstants.A256GCM.equals(jwaAlgorithmName)) { + expectedAesKeyLength = 32; + expectedCEKLength = 32; } else { expectedAesKeyLength = 0; expectedCEKLength = 0; diff --git a/services/src/main/java/org/keycloak/crypto/Aes192CbcHmacSha384ContentEncryptionProviderFactory.java b/services/src/main/java/org/keycloak/crypto/Aes192CbcHmacSha384ContentEncryptionProviderFactory.java new file mode 100644 index 0000000000..aef8395bc9 --- /dev/null +++ b/services/src/main/java/org/keycloak/crypto/Aes192CbcHmacSha384ContentEncryptionProviderFactory.java @@ -0,0 +1,36 @@ +/* + * Copyright 2020 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 Aes192CbcHmacSha384ContentEncryptionProviderFactory implements ContentEncryptionProviderFactory { + public static final String ID = JWEConstants.A192CBC_HS384; + + @Override + public String getId() { + return ID; + } + + @Override + public ContentEncryptionProvider create(KeycloakSession session) { + return new AesCbcHmacShaContentEncryptionProvider(session, ID); + } + +} diff --git a/services/src/main/java/org/keycloak/crypto/Aes192GcmContentEncryptionProviderFactory.java b/services/src/main/java/org/keycloak/crypto/Aes192GcmContentEncryptionProviderFactory.java new file mode 100644 index 0000000000..25795baa8f --- /dev/null +++ b/services/src/main/java/org/keycloak/crypto/Aes192GcmContentEncryptionProviderFactory.java @@ -0,0 +1,36 @@ +/* + * Copyright 2020 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 Aes192GcmContentEncryptionProviderFactory implements ContentEncryptionProviderFactory { + public static final String ID = JWEConstants.A192GCM; + + @Override + public String getId() { + return ID; + } + + @Override + public ContentEncryptionProvider create(KeycloakSession session) { + return new AesGcmContentEncryptionProvider(session, ID); + } + +} diff --git a/services/src/main/java/org/keycloak/crypto/Aes256CbcHmacSha512ContentEncryptionProviderFactory.java b/services/src/main/java/org/keycloak/crypto/Aes256CbcHmacSha512ContentEncryptionProviderFactory.java new file mode 100644 index 0000000000..17c8febab6 --- /dev/null +++ b/services/src/main/java/org/keycloak/crypto/Aes256CbcHmacSha512ContentEncryptionProviderFactory.java @@ -0,0 +1,36 @@ +/* + * Copyright 2020 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 Aes256CbcHmacSha512ContentEncryptionProviderFactory implements ContentEncryptionProviderFactory { + public static final String ID = JWEConstants.A256CBC_HS512; + + @Override + public String getId() { + return ID; + } + + @Override + public ContentEncryptionProvider create(KeycloakSession session) { + return new AesCbcHmacShaContentEncryptionProvider(session, ID); + } + +} diff --git a/services/src/main/java/org/keycloak/crypto/Aes256GcmContentEncryptionProviderFactory.java b/services/src/main/java/org/keycloak/crypto/Aes256GcmContentEncryptionProviderFactory.java new file mode 100644 index 0000000000..d2b1478a59 --- /dev/null +++ b/services/src/main/java/org/keycloak/crypto/Aes256GcmContentEncryptionProviderFactory.java @@ -0,0 +1,36 @@ +/* + * Copyright 2020 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 Aes256GcmContentEncryptionProviderFactory implements ContentEncryptionProviderFactory { + public static final String ID = JWEConstants.A256GCM; + + @Override + public String getId() { + return ID; + } + + @Override + public ContentEncryptionProvider create(KeycloakSession session) { + return new AesGcmContentEncryptionProvider(session, ID); + } + +} diff --git a/services/src/main/resources/META-INF/services/org.keycloak.crypto.ContentEncryptionProviderFactory b/services/src/main/resources/META-INF/services/org.keycloak.crypto.ContentEncryptionProviderFactory index bf03c473c1..6776390d44 100644 --- a/services/src/main/resources/META-INF/services/org.keycloak.crypto.ContentEncryptionProviderFactory +++ b/services/src/main/resources/META-INF/services/org.keycloak.crypto.ContentEncryptionProviderFactory @@ -1,2 +1,6 @@ org.keycloak.crypto.Aes128CbcHmacSha256ContentEncryptionProviderFactory -org.keycloak.crypto.Aes128GcmContentEncryptionProviderFactory \ No newline at end of file +org.keycloak.crypto.Aes192CbcHmacSha384ContentEncryptionProviderFactory +org.keycloak.crypto.Aes256CbcHmacSha512ContentEncryptionProviderFactory +org.keycloak.crypto.Aes128GcmContentEncryptionProviderFactory +org.keycloak.crypto.Aes192GcmContentEncryptionProviderFactory +org.keycloak.crypto.Aes256GcmContentEncryptionProviderFactory \ No newline at end of file diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oidc/IdTokenEncryptionTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oidc/IdTokenEncryptionTest.java index 7c0c2eb9fd..cddef04c83 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oidc/IdTokenEncryptionTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oidc/IdTokenEncryptionTest.java @@ -109,11 +109,31 @@ public class IdTokenEncryptionTest extends AbstractTestRealmKeycloakTest { testIdTokenSignatureAndEncryption(Algorithm.ES256, JWEConstants.RSA1_5, JWEConstants.A128CBC_HS256); } + @Test + public void testIdTokenEncryptionAlgRSA1_5EncA192CBC_HS384() { + testIdTokenSignatureAndEncryption(Algorithm.PS256, JWEConstants.RSA1_5, JWEConstants.A192CBC_HS384); + } + + @Test + public void testIdTokenEncryptionAlgRSA1_5EncA256CBC_HS512() { + testIdTokenSignatureAndEncryption(Algorithm.PS384, JWEConstants.RSA1_5, JWEConstants.A256CBC_HS512); + } + @Test public void testIdTokenEncryptionAlgRSA1_5EncA128GCM() { testIdTokenSignatureAndEncryption(Algorithm.RS384, JWEConstants.RSA1_5, JWEConstants.A128GCM); } + @Test + public void testIdTokenEncryptionAlgRSA1_5EncA192GCM() { + testIdTokenSignatureAndEncryption(Algorithm.RS512, JWEConstants.RSA1_5, JWEConstants.A192GCM); + } + + @Test + public void testIdTokenEncryptionAlgRSA1_5EncA256GCM() { + testIdTokenSignatureAndEncryption(Algorithm.RS256, JWEConstants.RSA1_5, JWEConstants.A256GCM); + } + @Test public void testIdTokenEncryptionAlgRSA_OAEPEncA128CBC_HS256() { // add key provider explicitly though DefaultKeyManager create fallback key provider if not exist @@ -121,6 +141,16 @@ public class IdTokenEncryptionTest extends AbstractTestRealmKeycloakTest { testIdTokenSignatureAndEncryption(Algorithm.ES512, JWEConstants.RSA_OAEP, JWEConstants.A128CBC_HS256); } + @Test + public void testIdTokenEncryptionAlgRSA_OAEPEncA192CBC_HS384() { + testIdTokenSignatureAndEncryption(Algorithm.PS256, JWEConstants.RSA_OAEP, JWEConstants.A192CBC_HS384); + } + + @Test + public void testIdTokenEncryptionAlgRSA_OAEPEncA256CBC_HS512() { + testIdTokenSignatureAndEncryption(Algorithm.PS512, JWEConstants.RSA_OAEP, JWEConstants.A256CBC_HS512); + } + @Test public void testIdTokenEncryptionAlgRSA_OAEPEncA128GCM() { // add key provider explicitly though DefaultKeyManager create fallback key provider if not exist @@ -128,6 +158,16 @@ public class IdTokenEncryptionTest extends AbstractTestRealmKeycloakTest { testIdTokenSignatureAndEncryption(Algorithm.ES256, JWEConstants.RSA_OAEP, JWEConstants.A128GCM); } + @Test + public void testIdTokenEncryptionAlgRSA_OAEPEncA192GCM() { + testIdTokenSignatureAndEncryption(Algorithm.PS384, JWEConstants.RSA_OAEP, JWEConstants.A192GCM); + } + + @Test + public void testIdTokenEncryptionAlgRSA_OAEPEncA256GCM() { + testIdTokenSignatureAndEncryption(Algorithm.PS512, JWEConstants.RSA_OAEP, JWEConstants.A256GCM); + } + private void testIdTokenSignatureAndEncryption(String sigAlgorithm, String algAlgorithm, String encAlgorithm) { ClientResource clientResource = null; ClientRepresentation clientRep = null; @@ -198,10 +238,17 @@ public class IdTokenEncryptionTest extends AbstractTestRealmKeycloakTest { } private JWEEncryptionProvider getJweEncryptionProvider(String encAlgorithm) { JWEEncryptionProvider jweEncryptionProvider = null; - if (JWEConstants.A128CBC_HS256.equals(encAlgorithm)) { - jweEncryptionProvider = new AesCbcHmacShaContentEncryptionProvider(null, encAlgorithm).jweEncryptionProvider(); - } else if (JWEConstants.A128GCM.equals(encAlgorithm)) { - jweEncryptionProvider = new AesGcmContentEncryptionProvider(null, encAlgorithm).jweEncryptionProvider(); + switch(encAlgorithm) { + case JWEConstants.A128GCM: + case JWEConstants.A192GCM: + case JWEConstants.A256GCM: + jweEncryptionProvider = new AesGcmContentEncryptionProvider(null, encAlgorithm).jweEncryptionProvider(); + break; + case JWEConstants.A128CBC_HS256: + case JWEConstants.A192CBC_HS384: + case JWEConstants.A256CBC_HS512: + jweEncryptionProvider = new AesCbcHmacShaContentEncryptionProvider(null, encAlgorithm).jweEncryptionProvider(); + break; } return jweEncryptionProvider; } 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 d92148c2db..16e0e175e6 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 @@ -133,7 +133,7 @@ public class OIDCWellKnownProviderTest extends AbstractKeycloakTest { // Encryption algorithms Assert.assertNames(oidcConfig.getIdTokenEncryptionAlgValuesSupported(), JWEConstants.RSA1_5, JWEConstants.RSA_OAEP); - Assert.assertNames(oidcConfig.getIdTokenEncryptionEncValuesSupported(), JWEConstants.A128CBC_HS256, JWEConstants.A128GCM); + Assert.assertNames(oidcConfig.getIdTokenEncryptionEncValuesSupported(), JWEConstants.A128CBC_HS256, JWEConstants.A128GCM, JWEConstants.A192CBC_HS384, JWEConstants.A192GCM, JWEConstants.A256CBC_HS512, JWEConstants.A256GCM); // Client authentication Assert.assertNames(oidcConfig.getTokenEndpointAuthMethodsSupported(), "client_secret_basic", "client_secret_post", "private_key_jwt", "client_secret_jwt", "tls_client_auth");