Skip to content

Commit e0bba39

Browse files
rmartincmposolda
authored andcommitted
Allow configure encryption details for SAML clients
Closes #40933 Signed-off-by: rmartinc <[email protected]>
1 parent 52b1c18 commit e0bba39

File tree

30 files changed

+883
-52
lines changed

30 files changed

+883
-52
lines changed

docs/documentation/server_admin/topics/clients/saml/proc-creating-saml-client.adoc

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,8 @@ This option applies to REDIRECT bindings where the signature is transferred in q
7878
+
7979
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.
8080

81+
*Allow ECP Flow*:: If true, this application is allowed to use SAML ECP profile for authentication.
82+
8183
=== Signature and Encryption
8284

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

103+
*Encryption algorithm*:: Encryption algorithm used for the client. Default value is `AES_256_GCM` when not defined.
104+
105+
*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.
106+
107+
*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.
108+
109+
*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.
110+
111+
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.
112+
101113
=== Login settings
102114

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

124136
== Keys tab
125137

126-
*Encrypt Assertions*:: Encrypts the assertions in SAML documents with the realms private key. The AES algorithm uses a key size of 128 bits.
127-
128138
*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.
129139

130-
*Allow ECP Flow*:: If true, this application is allowed to use SAML ECP profile for authentication.
140+
*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.
131141

132142
== Advanced tab
133143

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

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

170+
=== Advanced settings
171+
172+
*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.

docs/documentation/upgrading/topics/changes/changes-26_4_0.adoc

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,14 @@ The feature is enabled for a realm, if `Update Email` required action is enabled
5555
The feature slightly changes behaviour from previous versions when updating the profile during the authentication flow (e.g. when running the `UPDATE_PROFILE` required action).
5656
If an existing user does have an email set when updating the profile during the authentication flow, the email attribute will not be available.
5757

58+
=== Encryption algorithms for SAML updated
59+
60+
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.
61+
62+
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.
63+
64+
For more information about client configuration, please see link:{adminguide_link}#_client-saml-configuration[Creating a SAML client] chapter in the {adminguide_name}.
65+
5866
// ------------------------ Deprecated features ------------------------ //
5967
== Deprecated features
6068

js/apps/admin-ui/maven-resources/theme/keycloak.v2/admin/messages/messages_en.properties

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1592,7 +1592,7 @@ synchronizationSettings=Synchronization settings
15921592
certificateHelp=Client Certificate for validate JWT issued by client and signed by Client private key from your keystore.
15931593
resetPasswordError=Error resetting password\: {{error}}
15941594
associatedPermissions=Associated permission
1595-
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.
1595+
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".
15961596
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.
15971597
createScopeBasedPermission=Create scope-based permission
15981598
showMore=Show more
@@ -1917,7 +1917,7 @@ eventTypes.IDENTITY_PROVIDER_LOGIN_ERROR.name=Identity provider login error
19171917
scopePermissions.groups.view-description=Policies that decide if an administrator can view this group
19181918
tokens=Tokens
19191919
createFlow=Create flow
1920-
encryptAssertionsHelp=Should SAML assertions be encrypted with client's public key using AES?
1920+
encryptAssertionsHelp=Should SAML assertions be encrypted with client's public key?
19211921
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.
19221922
unsavedChangesConfirm=You have unsaved changes. Do you really want to leave the page?
19231923
disabledOff=Disabled off
@@ -3501,9 +3501,17 @@ givenNameClaim=Given name Claim
35013501
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`.
35023502
familyNameClaim=Family name Claim
35033503
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`.
3504+
samlClientEncryptionAlgorithm=Encryption algorithm
3505+
samlClientEncryptionAlgorithmHelp=Encryption algorithm used for the client. Default AES_256_GCM.
3506+
samlClientKeyEncryptionAlgorithm=Key transport algorithm
3507+
samlClientKeyEncryptionAlgorithmHelp=Key transport algorithm used for the client to encrypt the secret key used for encryption. Default value RSA-OAEP-11.
3508+
samlClientEncryptionDigestMethod=Digest method for RSA-OAEP
3509+
samlClientEncryptionDigestMethodHelp=Digest method to use when any RSA-OAEP algorithm is selected as the key transport algorithm. Default value SHA-256.
3510+
samlClientEncryptionMaskGenerationFunction=Mask generation function
3511+
samlClientEncryptionMaskGenerationFunctionHelp=Mask generation function to use when RSA-OAEP-11 is selected as the key transport algorithm. Default value mgf1sha256.
35043512
openIdVerifiableCredentials=OpenID for Verifiable Credentials
35053513
openIdVerifiableCredentialsHelp=This section is used to configure settings related to OpenID for Verifiable Credential Issuance (OID4VCI).
35063514
oid4vciEnabled=Enable OID4VCI
35073515
oid4vciEnabledHelp=Enable this option to allow the client to request verifiable credentials from Keycloak's OID4VCI credential endpoint.
35083516
noAccessPolicies=No access policies
3509-
noAccessPoliciesInstructions=There haven't been configured any access policies yet. Click the button below to configure the first policy.
3517+
noAccessPoliciesInstructions=There haven't been configured any access policies yet. Click the button below to configure the first policy.
Lines changed: 167 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
import { SelectControl } from "@keycloak/keycloak-ui-shared";
2+
import { useEffect } from "react";
3+
import { useFormContext } from "react-hook-form";
4+
import { useTranslation } from "react-i18next";
5+
import { convertAttributeNameToForm } from "../../util";
6+
import { FormFields } from "../ClientDetails";
7+
8+
export const SamlEncryption = () => {
9+
const { t } = useTranslation();
10+
const { watch, setValue } = useFormContext();
11+
const ALGORITHM_RSA_OAEP = "http://www.w3.org/2009/xmlenc11#rsa-oaep";
12+
const ALGORITHM_RSA_OAEP_MGF1P =
13+
"http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p";
14+
const keyEncryptionAlgorithm = watch(
15+
convertAttributeNameToForm<FormFields>(
16+
"attributes.saml.encryption.keyAlgorithm",
17+
),
18+
"",
19+
);
20+
21+
// remove optional fields if not displayed
22+
useEffect(() => {
23+
if (keyEncryptionAlgorithm !== ALGORITHM_RSA_OAEP) {
24+
setValue(
25+
convertAttributeNameToForm<FormFields>(
26+
"attributes.saml.encryption.maskGenerationFunction",
27+
),
28+
"",
29+
);
30+
if (keyEncryptionAlgorithm !== ALGORITHM_RSA_OAEP_MGF1P) {
31+
setValue(
32+
convertAttributeNameToForm<FormFields>(
33+
"attributes.saml.encryption.digestMethod",
34+
),
35+
"",
36+
);
37+
}
38+
}
39+
}, [keyEncryptionAlgorithm]);
40+
41+
return (
42+
<>
43+
<SelectControl
44+
name={convertAttributeNameToForm<FormFields>(
45+
"attributes.saml.encryption.algorithm",
46+
)}
47+
label={t("samlClientEncryptionAlgorithm")}
48+
labelIcon={t("samlClientEncryptionAlgorithmHelp")}
49+
controller={{
50+
defaultValue: "",
51+
}}
52+
options={[
53+
{ key: "", value: t("choose") },
54+
{
55+
key: "http://www.w3.org/2009/xmlenc11#aes256-gcm",
56+
value: "AES_256_GCM",
57+
},
58+
{
59+
key: "http://www.w3.org/2009/xmlenc11#aes192-gcm",
60+
value: "AES_192_GCM",
61+
},
62+
{
63+
key: "http://www.w3.org/2009/xmlenc11#aes128-gcm",
64+
value: "AES_128_GCM",
65+
},
66+
{
67+
key: "http://www.w3.org/2001/04/xmlenc#aes256-cbc",
68+
value: "AES_256_CBC",
69+
},
70+
{
71+
key: "http://www.w3.org/2001/04/xmlenc#aes192-cbc",
72+
value: "AES_192_CBC",
73+
},
74+
{
75+
key: "http://www.w3.org/2001/04/xmlenc#aes128-cbc",
76+
value: "AES_128_CBC",
77+
},
78+
]}
79+
/>
80+
81+
<SelectControl
82+
name={convertAttributeNameToForm<FormFields>(
83+
"attributes.saml.encryption.keyAlgorithm",
84+
)}
85+
label={t("samlClientKeyEncryptionAlgorithm")}
86+
labelIcon={t("samlClientKeyEncryptionAlgorithmHelp")}
87+
controller={{
88+
defaultValue: "",
89+
}}
90+
options={[
91+
{ key: "", value: t("choose") },
92+
{
93+
key: ALGORITHM_RSA_OAEP,
94+
value: "RSA-OAEP-11",
95+
},
96+
{
97+
key: ALGORITHM_RSA_OAEP_MGF1P,
98+
value: "RSA-OAEP-MGF1P",
99+
},
100+
{ key: "http://www.w3.org/2001/04/xmlenc#rsa-1_5", value: "RSA1_5" },
101+
]}
102+
/>
103+
104+
{(keyEncryptionAlgorithm === ALGORITHM_RSA_OAEP ||
105+
keyEncryptionAlgorithm === ALGORITHM_RSA_OAEP_MGF1P) && (
106+
<SelectControl
107+
name={convertAttributeNameToForm<FormFields>(
108+
"attributes.saml.encryption.digestMethod",
109+
)}
110+
label={t("samlClientEncryptionDigestMethod")}
111+
labelIcon={t("samlClientEncryptionDigestMethodHelp")}
112+
controller={{
113+
defaultValue: "",
114+
}}
115+
options={[
116+
{ key: "", value: t("choose") },
117+
{
118+
key: "http://www.w3.org/2001/04/xmlenc#sha512",
119+
value: "SHA-512",
120+
},
121+
{
122+
key: "http://www.w3.org/2001/04/xmlenc#sha256",
123+
value: "SHA-256",
124+
},
125+
{ key: "http://www.w3.org/2000/09/xmldsig#sha1", value: "SHA-1" },
126+
]}
127+
/>
128+
)}
129+
130+
{keyEncryptionAlgorithm === ALGORITHM_RSA_OAEP && (
131+
<SelectControl
132+
name={convertAttributeNameToForm<FormFields>(
133+
"attributes.saml.encryption.maskGenerationFunction",
134+
)}
135+
label={t("samlClientEncryptionMaskGenerationFunction")}
136+
labelIcon={t("samlClientEncryptionMaskGenerationFunctionHelp")}
137+
controller={{
138+
defaultValue: "",
139+
}}
140+
options={[
141+
{ key: "", value: t("choose") },
142+
{
143+
key: "http://www.w3.org/2009/xmlenc11#mgf1sha512",
144+
value: "mgf1sha512",
145+
},
146+
{
147+
key: "http://www.w3.org/2009/xmlenc11#mgf1sha384",
148+
value: "mgf1sha384",
149+
},
150+
{
151+
key: "http://www.w3.org/2009/xmlenc11#mgf1sha256",
152+
value: "mgf1sha256",
153+
},
154+
{
155+
key: "http://www.w3.org/2009/xmlenc11#mgf1sha224",
156+
value: "mgf1sha224",
157+
},
158+
{
159+
key: "http://www.w3.org/2009/xmlenc11#mgf1sha1",
160+
value: "mgf1sha1",
161+
},
162+
]}
163+
/>
164+
)}
165+
</>
166+
);
167+
};

js/apps/admin-ui/src/clients/add/SamlSignature.tsx

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { FormAccess } from "../../components/form/FormAccess";
55
import { convertAttributeNameToForm } from "../../util";
66
import { FormFields } from "../ClientDetails";
77
import { Toggle } from "./SamlConfig";
8+
import { SamlEncryption } from "./SamlEncryption";
89

910
export const SIGNATURE_ALGORITHMS = [
1011
"RSA_SHA1",
@@ -45,6 +46,10 @@ export const SamlSignature = () => {
4546
"attributes.saml.assertion.signature",
4647
),
4748
);
49+
const samlEncryption = watch(
50+
convertAttributeNameToForm<FormFields>("attributes.saml.encrypt"),
51+
"false",
52+
);
4853

4954
return (
5055
<FormAccess
@@ -96,6 +101,7 @@ export const SamlSignature = () => {
96101
value: name,
97102
}))}
98103
/>
104+
{samlEncryption === "true" && <SamlEncryption />}
99105
</>
100106
)}
101107
</FormAccess>

js/apps/admin-ui/src/clients/keys/SamlKeys.tsx

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,19 +38,35 @@ type SamlKeysProps = {
3838
save: () => void;
3939
};
4040

41+
type KeyMapping = {
42+
name: string;
43+
title: string;
44+
key: string;
45+
relatedKeys: string[];
46+
};
47+
4148
const KEYS = ["saml.signing", "saml.encryption"] as const;
4249
export type KeyTypes = (typeof KEYS)[number];
4350

44-
const KEYS_MAPPING: { [key in KeyTypes]: { [index: string]: string } } = {
51+
const KEYS_MAPPING: { [key in KeyTypes]: KeyMapping } = {
4552
"saml.signing": {
4653
name: convertAttributeNameToForm("attributes.saml.client.signature"),
4754
title: "signingKeysConfig",
4855
key: "clientSignature",
56+
relatedKeys: [],
4957
},
5058
"saml.encryption": {
5159
name: convertAttributeNameToForm("attributes.saml.encrypt"),
5260
title: "encryptionKeysConfig",
5361
key: "encryptAssertions",
62+
relatedKeys: [
63+
convertAttributeNameToForm("attributes.saml.encryption.algorithm"),
64+
convertAttributeNameToForm("attributes.saml.encryption.keyAlgorithm"),
65+
convertAttributeNameToForm("attributes.saml.encryption.digestMethod"),
66+
convertAttributeNameToForm(
67+
"attributes.saml.encryption.maskGenerationFunction",
68+
),
69+
],
5470
},
5571
};
5672

@@ -215,6 +231,9 @@ export const SamlKeys = ({ clientId, save }: SamlKeysProps) => {
215231
cancelButtonLabel: "no",
216232
onConfirm: () => {
217233
setValue(KEYS_MAPPING[selectedType!].name, "false");
234+
for (const key of KEYS_MAPPING[selectedType!].relatedKeys) {
235+
setValue(key, ""); // remove related attributes when disabled
236+
}
218237
save();
219238
},
220239
});
@@ -237,6 +256,9 @@ export const SamlKeys = ({ clientId, save }: SamlKeysProps) => {
237256
attr={isChanged}
238257
onClose={() => {
239258
setIsChanged(undefined);
259+
for (const key of KEYS_MAPPING[selectedType!].relatedKeys) {
260+
setValue(key, ""); // take defaults when enabled
261+
}
240262
save();
241263
setRefresh(refresh + 1);
242264
}}
@@ -262,7 +284,10 @@ export const SamlKeys = ({ clientId, save }: SamlKeysProps) => {
262284
clientId={clientId}
263285
keyInfo={keyInfo?.[index]}
264286
attr={attr}
265-
onChanged={setIsChanged}
287+
onChanged={(type) => {
288+
setIsChanged(type);
289+
setSelectedType(type);
290+
}}
266291
onGenerate={(type, isNew) => {
267292
setSelectedType(type);
268293
if (!isNew) {

0 commit comments

Comments
 (0)