diff --git a/docs/documentation/release_notes/topics/22_0_0.adoc b/docs/documentation/release_notes/topics/22_0_0.adoc index f08629b2e7..ce52071004 100644 --- a/docs/documentation/release_notes/topics/22_0_0.adoc +++ b/docs/documentation/release_notes/topics/22_0_0.adoc @@ -149,3 +149,7 @@ picked (`Always`), or the global JVM one (`Never`). Deployments where `Only for ldaps` was used will automatically behave as if `Always` option was selected for TLS-secured LDAP connections. + += Support for JWE encrypted ID Tokens and UserInfo responses in OpenID Connect providers + +The OpenID Connect providers now support https://datatracker.ietf.org/doc/html/rfc7516[Json Web Encryption (JWE)] for the ID Token and the UserInfo response. The providers use the realm keys defined for the selected encryption algorithm to perform the decryption. diff --git a/docs/documentation/server_admin/topics/identity-broker/oidc.adoc b/docs/documentation/server_admin/topics/identity-broker/oidc.adoc index 4c56ca16b9..2fc28440a1 100644 --- a/docs/documentation/server_admin/topics/identity-broker/oidc.adoc +++ b/docs/documentation/server_admin/topics/identity-broker/oidc.adoc @@ -78,3 +78,5 @@ If the user is unauthenticated in the IDP, the client still receives a `login_re |=== You can import all this configuration data by providing a URL or file that points to OpenID Provider Metadata. If you connect to a {project_name} external IDP, you can import the IDP settings from `{kc_realms_path}/{realm-name}/.well-known/openid-configuration`. This link is a JSON document describing metadata about the IDP. + +If you want to use https://datatracker.ietf.org/doc/html/rfc7516[Json Web Encryption (JWE)] ID Tokens or UserInfo responses in the provider, the IDP needs to know the public key to use with {project_name}. The provider uses the <> defined for the different encryption algorithms to decrypt the tokens. {project_name} provides a standard xref:con-server-oidc-uri-endpoints_{context}[JWKS endpoint] which the IDP can use for downloading the keys automatically. \ No newline at end of file diff --git a/services/src/main/java/org/keycloak/broker/oidc/OIDCIdentityProvider.java b/services/src/main/java/org/keycloak/broker/oidc/OIDCIdentityProvider.java index 5217a03465..83b371aae9 100755 --- a/services/src/main/java/org/keycloak/broker/oidc/OIDCIdentityProvider.java +++ b/services/src/main/java/org/keycloak/broker/oidc/OIDCIdentityProvider.java @@ -32,14 +32,18 @@ import org.keycloak.common.util.Base64Url; import org.keycloak.common.util.SecretGenerator; import org.keycloak.common.util.Time; import org.keycloak.connections.httpclient.HttpClientProvider; +import org.keycloak.crypto.KeyUse; import org.keycloak.crypto.KeyWrapper; import org.keycloak.crypto.SignatureProvider; import org.keycloak.events.Details; import org.keycloak.events.Errors; import org.keycloak.events.EventBuilder; import org.keycloak.events.EventType; +import org.keycloak.jose.JOSE; +import org.keycloak.jose.JOSEParser; +import org.keycloak.jose.jwe.JWE; +import org.keycloak.jose.jwe.JWEException; import org.keycloak.jose.jws.JWSInput; -import org.keycloak.jose.jws.JWSInputException; import org.keycloak.keys.loader.PublicKeyStorageManager; import org.keycloak.models.AbstractKeycloakTransaction; import org.keycloak.models.ClientModel; @@ -484,19 +488,7 @@ public class OIDCIdentityProvider extends AbstractOAuth2IdentityProviderTests the broker using a JWE encrypted token for id token and user info. The test + * can be extended to use different algorithms. The default uses RSA-OAEP as + * encryption key management algorithm, A256GCM as the content encryption + * algorithm and RS512 as the signature algorithm.

+ * + * @author rmartinc + */ +public class KcOidcBrokerJWETest extends AbstractBrokerTest { + + private final String encAlg; + private final String encEnc; + private final String sigAlg; + + public KcOidcBrokerJWETest() { + this(JWEConstants.RSA_OAEP, JWEConstants.A256GCM, Algorithm.RS512); + } + + protected KcOidcBrokerJWETest(String encAlg, String encEnc, String sigAlg) { + this.encAlg = encAlg; + this.encEnc = encEnc; + this.sigAlg = sigAlg; + } + + @Override + protected BrokerConfiguration getBrokerConfiguration() { + return new KcOidcBrokerConfiguration() { + @Override + public List createProviderClients() { + List clientsRepList = super.createProviderClients(); + for (ClientRepresentation client : clientsRepList) { + Map attrs = client.getAttributes(); + + // use the certs from the consumer realm to perform the encryption + attrs.put(OIDCConfigAttributes.USE_JWKS_URL, "true"); + attrs.put(OIDCConfigAttributes.JWKS_URL, BrokerTestTools.getConsumerRoot() + + "/auth/realms/" + BrokerTestConstants.REALM_CONS_NAME + "/protocol/openid-connect/certs"); + + // assign the encryption and signature attributes + if (encAlg != null) { + attrs.put(OIDCConfigAttributes.ID_TOKEN_ENCRYPTED_RESPONSE_ALG, encAlg); + attrs.put(OIDCConfigAttributes.USER_INFO_ENCRYPTED_RESPONSE_ALG, encAlg); + } + + if (encEnc != null) { + attrs.put(OIDCConfigAttributes.ID_TOKEN_ENCRYPTED_RESPONSE_ENC, encEnc); + attrs.put(OIDCConfigAttributes.USER_INFO_ENCRYPTED_RESPONSE_ENC, encEnc); + } + + if (sigAlg != null) { + attrs.put(OIDCConfigAttributes.ID_TOKEN_SIGNED_RESPONSE_ALG, sigAlg); + attrs.put(OIDCConfigAttributes.USER_INFO_RESPONSE_SIGNATURE_ALG, sigAlg); + } + } + return clientsRepList; + } + + @Override + public RealmRepresentation createConsumerRealm() { + RealmRepresentation realm = super.createConsumerRealm(); + + if (encAlg != null) { + // create the RSA component for the encryption in the specified alg + ComponentExportRepresentation component = new ComponentExportRepresentation(); + component.setName("rsa-enc-generated"); + component.setProviderId("rsa-enc-generated"); + + MultivaluedHashMap config = new MultivaluedHashMap<>(); + config.putSingle("priority", DefaultKeyProviders.DEFAULT_PRIORITY); + config.putSingle("keyUse", KeyUse.ENC.name()); + config.putSingle("algorithm", encAlg); + component.setConfig(config); + + MultivaluedHashMap components = realm.getComponents(); + if (components == null) { + components = new MultivaluedHashMap<>(); + realm.setComponents(components); + } + components.add(KeyProvider.class.getName(), component); + } + + return realm; + } + }; + } +} diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOidcBrokerJWEUserInfoJustEncryptedTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOidcBrokerJWEUserInfoJustEncryptedTest.java new file mode 100644 index 0000000000..f57872cc7a --- /dev/null +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOidcBrokerJWEUserInfoJustEncryptedTest.java @@ -0,0 +1,33 @@ +/* + * Copyright 2023 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.testsuite.broker; + +import org.keycloak.jose.jwe.JWEConstants; + +/** + *

Extension of the KcOidcBrokerJWETest test to use a different key algorithm (RSA1_5), + * the default content encryption algorithm (A128CBC-HS256) and the default signature + * algorithm (RS256 for id token and none/unsigned for user info).

+ * + * @author rmartinc + */ +public class KcOidcBrokerJWEUserInfoJustEncryptedTest extends KcOidcBrokerJWETest { + + public KcOidcBrokerJWEUserInfoJustEncryptedTest() { + super(JWEConstants.RSA1_5, null, null); + } +} diff --git a/testsuite/integration-arquillian/tests/base/testsuites/fips-suite b/testsuite/integration-arquillian/tests/base/testsuites/fips-suite index eca4400478..a4b646c153 100644 --- a/testsuite/integration-arquillian/tests/base/testsuites/fips-suite +++ b/testsuite/integration-arquillian/tests/base/testsuites/fips-suite @@ -17,6 +17,8 @@ KcAdmTest KcAdmCreateTest SAMLServletAdapterTest SamlSignatureTest +KcOidcBrokerJWETest +KcOidcBrokerJWEUserInfoJustEncryptedTest KcSamlBrokerTest KcSamlFirstBrokerLoginTest KcSamlEncryptedIdTest