KEYCLOAK-13104 Signed and Encrypted ID Token Support : AES 192bit and 256bit key support

This commit is contained in:
Takashi Norimatsu 2020-03-01 21:01:46 +09:00 committed by Marek Posolda
parent 54db691b26
commit c057b994e7
10 changed files with 221 additions and 7 deletions

View file

@ -29,6 +29,9 @@ public class JWEConstants {
public static final String A128CBC_HS256 = "A128CBC-HS256"; public static final String A128CBC_HS256 = "A128CBC-HS256";
public static final String A192CBC_HS384 = "A192CBC-HS384"; 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 A128GCM = "A128GCM";
public static final String A192GCM = "A192GCM";
public static final String A256GCM = "A256GCM";
} }

View file

@ -33,6 +33,16 @@ public class AesCbcHmacShaJWEEncryptionProvider extends AesCbcHmacShaEncryptionP
expectedAesKeyLength = 16; expectedAesKeyLength = 16;
hmacShaAlgorithm = "HMACSHA256"; hmacShaAlgorithm = "HMACSHA256";
authenticationTagLength = 16; 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 { } else {
expectedCEKLength = 0; expectedCEKLength = 0;
expectedAesKeyLength = 0; expectedAesKeyLength = 0;

View file

@ -29,6 +29,12 @@ public class AesGcmJWEEncryptionProvider extends AesGcmEncryptionProvider {
if (JWEConstants.A128GCM.equals(jwaAlgorithmName)) { if (JWEConstants.A128GCM.equals(jwaAlgorithmName)) {
expectedAesKeyLength = 16; expectedAesKeyLength = 16;
expectedCEKLength = 16; expectedCEKLength = 16;
} else if (JWEConstants.A192GCM.equals(jwaAlgorithmName)) {
expectedAesKeyLength = 24;
expectedCEKLength = 24;
} else if (JWEConstants.A256GCM.equals(jwaAlgorithmName)) {
expectedAesKeyLength = 32;
expectedCEKLength = 32;
} else { } else {
expectedAesKeyLength = 0; expectedAesKeyLength = 0;
expectedCEKLength = 0; expectedCEKLength = 0;

View file

@ -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);
}
}

View file

@ -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);
}
}

View file

@ -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);
}
}

View file

@ -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);
}
}

View file

@ -1,2 +1,6 @@
org.keycloak.crypto.Aes128CbcHmacSha256ContentEncryptionProviderFactory org.keycloak.crypto.Aes128CbcHmacSha256ContentEncryptionProviderFactory
org.keycloak.crypto.Aes192CbcHmacSha384ContentEncryptionProviderFactory
org.keycloak.crypto.Aes256CbcHmacSha512ContentEncryptionProviderFactory
org.keycloak.crypto.Aes128GcmContentEncryptionProviderFactory org.keycloak.crypto.Aes128GcmContentEncryptionProviderFactory
org.keycloak.crypto.Aes192GcmContentEncryptionProviderFactory
org.keycloak.crypto.Aes256GcmContentEncryptionProviderFactory

View file

@ -109,11 +109,31 @@ public class IdTokenEncryptionTest extends AbstractTestRealmKeycloakTest {
testIdTokenSignatureAndEncryption(Algorithm.ES256, JWEConstants.RSA1_5, JWEConstants.A128CBC_HS256); 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 @Test
public void testIdTokenEncryptionAlgRSA1_5EncA128GCM() { public void testIdTokenEncryptionAlgRSA1_5EncA128GCM() {
testIdTokenSignatureAndEncryption(Algorithm.RS384, JWEConstants.RSA1_5, JWEConstants.A128GCM); 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 @Test
public void testIdTokenEncryptionAlgRSA_OAEPEncA128CBC_HS256() { public void testIdTokenEncryptionAlgRSA_OAEPEncA128CBC_HS256() {
// add key provider explicitly though DefaultKeyManager create fallback key provider if not exist // 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); 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 @Test
public void testIdTokenEncryptionAlgRSA_OAEPEncA128GCM() { public void testIdTokenEncryptionAlgRSA_OAEPEncA128GCM() {
// add key provider explicitly though DefaultKeyManager create fallback key provider if not exist // 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); 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) { private void testIdTokenSignatureAndEncryption(String sigAlgorithm, String algAlgorithm, String encAlgorithm) {
ClientResource clientResource = null; ClientResource clientResource = null;
ClientRepresentation clientRep = null; ClientRepresentation clientRep = null;
@ -198,10 +238,17 @@ public class IdTokenEncryptionTest extends AbstractTestRealmKeycloakTest {
} }
private JWEEncryptionProvider getJweEncryptionProvider(String encAlgorithm) { private JWEEncryptionProvider getJweEncryptionProvider(String encAlgorithm) {
JWEEncryptionProvider jweEncryptionProvider = null; JWEEncryptionProvider jweEncryptionProvider = null;
if (JWEConstants.A128CBC_HS256.equals(encAlgorithm)) { switch(encAlgorithm) {
jweEncryptionProvider = new AesCbcHmacShaContentEncryptionProvider(null, encAlgorithm).jweEncryptionProvider(); case JWEConstants.A128GCM:
} else if (JWEConstants.A128GCM.equals(encAlgorithm)) { case JWEConstants.A192GCM:
jweEncryptionProvider = new AesGcmContentEncryptionProvider(null, encAlgorithm).jweEncryptionProvider(); 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; return jweEncryptionProvider;
} }

View file

@ -133,7 +133,7 @@ public class OIDCWellKnownProviderTest extends AbstractKeycloakTest {
// Encryption algorithms // Encryption algorithms
Assert.assertNames(oidcConfig.getIdTokenEncryptionAlgValuesSupported(), JWEConstants.RSA1_5, JWEConstants.RSA_OAEP); 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 // Client authentication
Assert.assertNames(oidcConfig.getTokenEndpointAuthMethodsSupported(), "client_secret_basic", "client_secret_post", "private_key_jwt", "client_secret_jwt", "tls_client_auth"); Assert.assertNames(oidcConfig.getTokenEndpointAuthMethodsSupported(), "client_secret_basic", "client_secret_post", "private_key_jwt", "client_secret_jwt", "tls_client_auth");