+
Skip to content

Allow configure encryption details for SAML clients #40937

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,8 @@ This option applies to REDIRECT bindings where the signature is transferred in q
+
This option is used when {project_name} server and adapter provide the IDP and SP. This option is only relevant when *Sign Documents* is set to ON.

*Allow ECP Flow*:: If true, this application is allowed to use SAML ECP profile for authentication.

=== Signature and Encryption

*Sign Documents*:: When set to ON, {project_name} signs the document using the realms private key.
Expand All @@ -98,6 +100,16 @@ do not work if the SAML client runs on Java 17 or higher.
+
*Canonicalization Method*:: The canonicalization method for XML signatures.

*Encryption algorithm*:: Encryption algorithm used for the client. Default value is `AES_256_GCM` when not defined.

*Key transport algorithm*:: Key transport algorithm used for the client to encrypt the secret key used for encryption. Default value is `RSA-OAEP-11` when not defined.

*Digest method for RSA-OAEP*:: Digest method to use when RSA-OAEP is selected as the key transport algorithm. Only available if *Key transport algorithm* is set to any RSA-OAEP algorithm. Default value is `SHA-256` when not defined.

*Mask generation function*:: Mask generation function to use when `RSA-OAEP-11` is selected as the key transport algorithm. Only available if *Key transport algorithm* is set to `RSA-OAEP-11` algorithm. Default value is `mgf1sha256` when no defined.

NOTE: The encryption options are only available if the *Encrypt Assertions* option is enabled in the *Keys* tab. For more information about SAML/XML encryption, see the link:https://www.w3.org/TR/xmlenc-core1/[XML Encryption Syntax and Processing] specification.

=== Login settings

*Login theme*:: A theme to use for login, OTP, grant registration, and forgotten password pages.
Expand All @@ -123,11 +135,9 @@ There will be also one item on the consent screen about this client itself.

== Keys tab

*Encrypt Assertions*:: Encrypts the assertions in SAML documents with the realms private key. The AES algorithm uses a key size of 128 bits.

*Client Signature Required*:: If *Client Signature Required* is enabled, documents coming from a client are expected to be signed. {project_name} will validate this signature using the client public key or cert set up in the `Keys` tab.

*Allow ECP Flow*:: If true, this application is allowed to use SAML ECP profile for authentication.
*Encrypt Assertions*:: Encrypts the assertions in SAML documents with the specified client public key. Default algorithms used for encryption are configured with security in mind. If you need a different configuration, the encryption details can be modified in the *Settings* tab, section *Signature and Encryption*. The encryption options are only visible when this *Encrypt Assertions* option is enabled.

== Advanced tab

Expand Down Expand Up @@ -157,3 +167,6 @@ This tab has many fields for specific situations. Some fields are covered in ot

*Artifact Resolution Service*:: URL of the client SOAP endpoint where to send the `ArtifactResolve` messages to.

=== Advanced settings

*Assertion Lifespan*:: Specific client lifespan set in the SAML assertion conditions. After that time the assertion will be invalid. If not specified the realm *Access Token Lifespan* is used. The `SessionNotOnOrAfter` attribute is not modified and continue using the *SSO Session Max* time defined at realm level.
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,14 @@ Starting with this release, {project_name} will cache by default only 10000 entr

Use the options `cache-embedded-offline-sessions-max-count` and `cache-embedded-offline-client-sessions-max-count` to change size of the offline session caches.

=== Encryption algorithms for SAML updated

When a SAML client was enabled to *Encrypt Assertions*, the assertion included in the SAML response was encrypted following the link:https://www.w3.org/TR/xmlenc-core1/[XML Encryption Syntax and Processing] specification. The algorithms used for encryption were fixed and outdated. Since this release, default encryption options are up to date and better suited in terms of security. Besides, the encryption details are also configurable, just in case a specific client needs a different set of algorithms to work properly. New attributes can be defined in the client to specify the exact algorithms used for encryption. The Admin console displays them in the client tab *Settings*, section *Signature and Encryption*, when the *Encrypt Assertions* option is enabled in the *Keys* tab.

In order to maintain backwards compatibility, {project_name}'s upgrade will modify the existing SAML clients to set the encryption attributes to work as before. This way old clients will receive the same encrypted assertion using the same previous algorithms. If the client supports the new default configuration, removing the attributes is recommended.

For more information about client configuration, please see link:{adminguide_link}#_client-saml-configuration[Creating a SAML client] chapter in the {adminguide_name}.

// ------------------------ Deprecated features ------------------------ //
== Deprecated features

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1592,7 +1592,7 @@ synchronizationSettings=Synchronization settings
certificateHelp=Client Certificate for validate JWT issued by client and signed by Client private key from your keystore.
resetPasswordError=Error resetting password\: {{error}}
associatedPermissions=Associated permission
encryptionKeysConfigExplain=If you enable the "Encryption assertions" below, you must configure the encryption keys by generating or importing keys, and the SAML assertions will be encrypted with the client's public key using AES.
encryptionKeysConfigExplain=If you enable the "Encryption assertions" below, you must configure the encryption keys by generating or importing keys, and the SAML assertions will be encrypted with the client's public key. When enabled, the encryption details can be modified in the "Settings" tab, section "Signature and Encryption".
preserveGroupInheritanceHelp=Flag whether group inheritance from LDAP should be propagated to Keycloak. If false, then all LDAP groups will be mapped as flat top-level groups in Keycloak. Otherwise group inheritance is preserved into Keycloak, but the group sync might fail if LDAP structure contains recursions or multiple parent groups per child groups.
createScopeBasedPermission=Create scope-based permission
showMore=Show more
Expand Down Expand Up @@ -1917,7 +1917,7 @@ eventTypes.IDENTITY_PROVIDER_LOGIN_ERROR.name=Identity provider login error
scopePermissions.groups.view-description=Policies that decide if an administrator can view this group
tokens=Tokens
createFlow=Create flow
encryptAssertionsHelp=Should SAML assertions be encrypted with client's public key using AES?
encryptAssertionsHelp=Should SAML assertions be encrypted with client's public key?
oAuthDPoPHelp=This enables support for Demonstrating Proof-of-Possession (DPoP) bound tokens. The access and refresh tokens are bound to the key stored on the user agent. In order to prove the possession of the key, the user agent must send a signed proof alongside the token.
unsavedChangesConfirm=You have unsaved changes. Do you really want to leave the page?
disabledOff=Disabled off
Expand Down Expand Up @@ -3499,3 +3499,12 @@ givenNameClaim=Given name Claim
givenNameClaimHelp=The name of the claim from the JSON document returned by the user profile endpoint representing the user's given name. If not provided, defaults to `given_name`.
familyNameClaim=Family name Claim
familyNameClaimHelp=The name of the claim from the JSON document returned by the user profile endpoint representing the user's family name. If not provided, defaults to `family_name`.
samlClientEncryptionAlgorithm=Encryption algorithm
samlClientEncryptionAlgorithmHelp=Encryption algorithm used for the client. Default AES_256_GCM.
samlClientKeyEncryptionAlgorithm=Key transport algorithm
samlClientKeyEncryptionAlgorithmHelp=Key transport algorithm used for the client to encrypt the secret key used for encryption. Default value RSA-OAEP-11.
samlClientEncryptionDigestMethod=Digest method for RSA-OAEP
samlClientEncryptionDigestMethodHelp=Digest method to use when any RSA-OAEP algorithm is selected as the key transport algorithm. Default value SHA-256.
samlClientEncryptionMaskGenerationFunction=Mask generation function
samlClientEncryptionMaskGenerationFunctionHelp=Mask generation function to use when RSA-OAEP-11 is selected as the key transport algorithm. Default value mgf1sha256.

166 changes: 166 additions & 0 deletions js/apps/admin-ui/src/clients/add/SamlEncryption.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
import { SelectControl } from "@keycloak/keycloak-ui-shared";
import { useFormContext } from "react-hook-form";
import { useTranslation } from "react-i18next";
import { convertAttributeNameToForm } from "../../util";
import { FormFields } from "../ClientDetails";

export const SamlEncryption = () => {
const { t } = useTranslation();
const { watch, setValue } = useFormContext();
const keyEncryptionAlgorithm = watch(
convertAttributeNameToForm<FormFields>(
"attributes.saml.encryption.keyAlgorithm",
),
"",
);

// remove optional fields if not displayed
if (keyEncryptionAlgorithm !== "http://www.w3.org/2009/xmlenc11#rsa-oaep") {
setValue(
convertAttributeNameToForm<FormFields>(
"attributes.saml.encryption.maskGenerationFunction",
),
"",
);
if (
keyEncryptionAlgorithm !==
"http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p"
) {
setValue(
convertAttributeNameToForm<FormFields>(
"attributes.saml.encryption.digestMethod",
),
"",
);
}
}

return (
<>
<SelectControl
name={convertAttributeNameToForm<FormFields>(
"attributes.saml.encryption.algorithm",
)}
label={t("samlClientEncryptionAlgorithm")}
labelIcon={t("samlClientEncryptionAlgorithmHelp")}
controller={{
defaultValue: "",
}}
options={[
{ key: "", value: t("choose") },
{
key: "http://www.w3.org/2009/xmlenc11#aes256-gcm",
value: "AES_256_GCM",
},
{
key: "http://www.w3.org/2009/xmlenc11#aes192-gcm",
value: "AES_192_GCM",
},
{
key: "http://www.w3.org/2009/xmlenc11#aes128-gcm",
value: "AES_128_GCM",
},
{
key: "http://www.w3.org/2001/04/xmlenc#aes256-cbc",
value: "AES_256_CBC",
},
{
key: "http://www.w3.org/2001/04/xmlenc#aes192-cbc",
value: "AES_192_CBC",
},
{
key: "http://www.w3.org/2001/04/xmlenc#aes128-cbc",
value: "AES_128_CBC",
},
]}
/>

<SelectControl
name={convertAttributeNameToForm<FormFields>(
"attributes.saml.encryption.keyAlgorithm",
)}
label={t("samlClientKeyEncryptionAlgorithm")}
labelIcon={t("samlClientKeyEncryptionAlgorithmHelp")}
controller={{
defaultValue: "",
}}
options={[
{ key: "", value: t("choose") },
{
key: "http://www.w3.org/2009/xmlenc11#rsa-oaep",
value: "RSA-OAEP-11",
},
{
key: "http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p",
value: "RSA-OAEP-MGF1P",
},
{ key: "http://www.w3.org/2001/04/xmlenc#rsa-1_5", value: "RSA1_5" },
]}
/>

{(keyEncryptionAlgorithm === "http://www.w3.org/2009/xmlenc11#rsa-oaep" ||
keyEncryptionAlgorithm ===
"http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p") && (
<SelectControl
name={convertAttributeNameToForm<FormFields>(
"attributes.saml.encryption.digestMethod",
)}
label={t("samlClientEncryptionDigestMethod")}
labelIcon={t("samlClientEncryptionDigestMethodHelp")}
controller={{
defaultValue: "",
}}
options={[
{ key: "", value: t("choose") },
{
key: "http://www.w3.org/2001/04/xmlenc#sha512",
value: "SHA-512",
},
{
key: "http://www.w3.org/2001/04/xmlenc#sha256",
value: "SHA-256",
},
{ key: "http://www.w3.org/2000/09/xmldsig#sha1", value: "SHA-1" },
]}
/>
)}

{keyEncryptionAlgorithm ===
"http://www.w3.org/2009/xmlenc11#rsa-oaep" && (
<SelectControl
name={convertAttributeNameToForm<FormFields>(
"attributes.saml.encryption.maskGenerationFunction",
)}
label={t("samlClientEncryptionMaskGenerationFunction")}
labelIcon={t("samlClientEncryptionMaskGenerationFunctionHelp")}
controller={{
defaultValue: "",
}}
options={[
{ key: "", value: t("choose") },
{
key: "http://www.w3.org/2009/xmlenc11#mgf1sha512",
value: "mgf1sha512",
},
{
key: "http://www.w3.org/2009/xmlenc11#mgf1sha384",
value: "mgf1sha384",
},
{
key: "http://www.w3.org/2009/xmlenc11#mgf1sha256",
value: "mgf1sha256",
},
{
key: "http://www.w3.org/2009/xmlenc11#mgf1sha224",
value: "mgf1sha224",
},
{
key: "http://www.w3.org/2009/xmlenc11#mgf1sha1",
value: "mgf1sha1",
},
]}
/>
)}
</>
);
};
6 changes: 6 additions & 0 deletions js/apps/admin-ui/src/clients/add/SamlSignature.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { FormAccess } from "../../components/form/FormAccess";
import { convertAttributeNameToForm } from "../../util";
import { FormFields } from "../ClientDetails";
import { Toggle } from "./SamlConfig";
import { SamlEncryption } from "./SamlEncryption";

export const SIGNATURE_ALGORITHMS = [
"RSA_SHA1",
Expand Down Expand Up @@ -45,6 +46,10 @@ export const SamlSignature = () => {
"attributes.saml.assertion.signature",
),
);
const samlEncryption = watch(
convertAttributeNameToForm<FormFields>("attributes.saml.encrypt"),
"false",
);

return (
<FormAccess
Expand Down Expand Up @@ -96,6 +101,7 @@ export const SamlSignature = () => {
value: name,
}))}
/>
{samlEncryption?.toString() === "true" && <SamlEncryption />}
</>
)}
</FormAccess>
Expand Down
24 changes: 23 additions & 1 deletion js/apps/admin-ui/src/clients/keys/SamlKeys.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,19 +38,35 @@ type SamlKeysProps = {
save: () => void;
};

type KeyMapping = {
name: string;
title: string;
key: string;
relatedKeys: string[];
};

const KEYS = ["saml.signing", "saml.encryption"] as const;
export type KeyTypes = (typeof KEYS)[number];

const KEYS_MAPPING: { [key in KeyTypes]: { [index: string]: string } } = {
const KEYS_MAPPING: { [key in KeyTypes]: KeyMapping } = {
"saml.signing": {
name: convertAttributeNameToForm("attributes.saml.client.signature"),
title: "signingKeysConfig",
key: "clientSignature",
relatedKeys: [],
},
"saml.encryption": {
name: convertAttributeNameToForm("attributes.saml.encrypt"),
title: "encryptionKeysConfig",
key: "encryptAssertions",
relatedKeys: [
convertAttributeNameToForm("attributes.saml.encryption.algorithm"),
convertAttributeNameToForm("attributes.saml.encryption.keyAlgorithm"),
convertAttributeNameToForm("attributes.saml.encryption.digestMethod"),
convertAttributeNameToForm(
"attributes.saml.encryption.maskGenerationFunction",
),
],
},
};

Expand Down Expand Up @@ -215,6 +231,9 @@ export const SamlKeys = ({ clientId, save }: SamlKeysProps) => {
cancelButtonLabel: "no",
onConfirm: () => {
setValue(KEYS_MAPPING[selectedType!].name, "false");
for (const key of KEYS_MAPPING[selectedType!].relatedKeys) {
setValue(key, ""); // remove related attributes when disabled
}
save();
},
});
Expand All @@ -237,6 +256,9 @@ export const SamlKeys = ({ clientId, save }: SamlKeysProps) => {
attr={isChanged}
onClose={() => {
setIsChanged(undefined);
for (const key of KEYS_MAPPING[selectedType!].relatedKeys) {
setValue(key, ""); // take defaults when enabled
}
save();
setRefresh(refresh + 1);
}}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/*
* Copyright 2025 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.connections.jpa.updater.liquibase.custom;

import liquibase.exception.CustomChangeException;
import liquibase.statement.SqlStatement;
import liquibase.statement.core.RawParameterizedSqlStatement;

/**
*
* @author rmartinc
*/
public class JpaUpdate26_4_0_SamlEncryptionAttributes extends CustomKeycloakTask {

@Override
protected String getTaskId() {
return "Insert legacy encryption attributes in SAML clients";
}

@Override
protected void generateStatementsImpl() throws CustomChangeException {
statements.add(createInsertQueryForAttribute("saml.encryption.algorithm", "http://www.w3.org/2001/04/xmlenc#aes128-cbc"));
statements.add(createInsertQueryForAttribute("saml.encryption.keyAlgorithm", "http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p"));
statements.add(createInsertQueryForAttribute("saml.encryption.digestMethod", "http://www.w3.org/2000/09/xmldsig#sha1"));
}

private SqlStatement createInsertQueryForAttribute(String attribute, String value) {
final String clientTable = getTableName("CLIENT");
final String clientAttributesTable = getTableName("CLIENT_ATTRIBUTES");
return new RawParameterizedSqlStatement(
"INSERT INTO " + clientAttributesTable + " (CLIENT_ID,NAME,VALUE) " +
"SELECT ID, ?, ? FROM " + clientTable + " WHERE PROTOCOL = ? AND ID NOT IN " +
"(SELECT CLIENT_ID FROM " + clientAttributesTable + " WHERE NAME = ?)",
attribute, value, "saml", attribute
);
}
}
Loading
Loading
点击 这是indexloc提供的php浏览器服务,不要输入任何密码和下载