keycloak-aplcache
Changes
services/src/main/java/org/keycloak/protocol/saml/EntityDescriptorDescriptionConverter.java 1(+1 -0)
Details
diff --git a/services/src/main/java/org/keycloak/protocol/saml/EntityDescriptorDescriptionConverter.java b/services/src/main/java/org/keycloak/protocol/saml/EntityDescriptorDescriptionConverter.java
index 00caa11..cca12cb 100755
--- a/services/src/main/java/org/keycloak/protocol/saml/EntityDescriptorDescriptionConverter.java
+++ b/services/src/main/java/org/keycloak/protocol/saml/EntityDescriptorDescriptionConverter.java
@@ -101,6 +101,7 @@ public class EntityDescriptorDescriptionConverter implements ClientDescriptionCo
app.setFullScopeAllowed(true);
app.setProtocol(SamlProtocol.LOGIN_PROTOCOL);
attributes.put(SamlConfigAttributes.SAML_SERVER_SIGNATURE, SamlProtocol.ATTRIBUTE_TRUE_VALUE); // default to true
+ attributes.put(SamlConfigAttributes.SAML_SERVER_SIGNATURE_KEYINFO_EXT, SamlProtocol.ATTRIBUTE_FALSE_VALUE); // default to false
attributes.put(SamlConfigAttributes.SAML_SIGNATURE_ALGORITHM, SignatureAlgorithm.RSA_SHA256.toString());
attributes.put(SamlConfigAttributes.SAML_AUTHNSTATEMENT, SamlProtocol.ATTRIBUTE_TRUE_VALUE);
SPSSODescriptorType spDescriptorType = CoreConfigUtil.getSPDescriptor(entity);
diff --git a/services/src/main/java/org/keycloak/protocol/saml/SamlClient.java b/services/src/main/java/org/keycloak/protocol/saml/SamlClient.java
index 336da7b..ee5aaba 100755
--- a/services/src/main/java/org/keycloak/protocol/saml/SamlClient.java
+++ b/services/src/main/java/org/keycloak/protocol/saml/SamlClient.java
@@ -118,7 +118,14 @@ public class SamlClient extends ClientConfigResolver {
public void setRequiresRealmSignature(boolean val) {
client.setAttribute(SamlConfigAttributes.SAML_SERVER_SIGNATURE, Boolean.toString(val));
+ }
+
+ public boolean addExtensionsElementWithKeyInfo() {
+ return "true".equals(resolveAttribute(SamlConfigAttributes.SAML_SERVER_SIGNATURE_KEYINFO_EXT));
+ }
+ public void setAddExtensionsElementWithKeyInfo(boolean val) {
+ client.setAttribute(SamlConfigAttributes.SAML_SERVER_SIGNATURE_KEYINFO_EXT, Boolean.toString(val));
}
public boolean forcePostBinding() {
diff --git a/services/src/main/java/org/keycloak/protocol/saml/SamlClientTemplate.java b/services/src/main/java/org/keycloak/protocol/saml/SamlClientTemplate.java
index e5bc2fa..0af3be0 100755
--- a/services/src/main/java/org/keycloak/protocol/saml/SamlClientTemplate.java
+++ b/services/src/main/java/org/keycloak/protocol/saml/SamlClientTemplate.java
@@ -17,6 +17,7 @@
package org.keycloak.protocol.saml;
+import java.util.Objects;
import org.keycloak.models.ClientTemplateModel;
import org.keycloak.saml.SignatureAlgorithm;
@@ -89,7 +90,14 @@ public class SamlClientTemplate {
public void setRequiresRealmSignature(boolean val) {
clientTemplate.setAttribute(SamlConfigAttributes.SAML_SERVER_SIGNATURE, Boolean.toString(val));
+ }
+
+ public boolean addExtensionsElementWithKeyInfo() {
+ return Objects.equals("true", clientTemplate.getAttribute(SamlConfigAttributes.SAML_SERVER_SIGNATURE_KEYINFO_EXT));
+ }
+ public void setAddExtensionsElementWithKeyInfo(boolean val) {
+ clientTemplate.setAttribute(SamlConfigAttributes.SAML_SERVER_SIGNATURE_KEYINFO_EXT, Boolean.toString(val));
}
public boolean forcePostBinding() {
diff --git a/services/src/main/java/org/keycloak/protocol/saml/SamlConfigAttributes.java b/services/src/main/java/org/keycloak/protocol/saml/SamlConfigAttributes.java
index 3356c31..9837179 100755
--- a/services/src/main/java/org/keycloak/protocol/saml/SamlConfigAttributes.java
+++ b/services/src/main/java/org/keycloak/protocol/saml/SamlConfigAttributes.java
@@ -31,6 +31,7 @@ public interface SamlConfigAttributes {
String SAML_AUTHNSTATEMENT = "saml.authnstatement";
String SAML_FORCE_NAME_ID_FORMAT_ATTRIBUTE = "saml_force_name_id_format";
String SAML_SERVER_SIGNATURE = "saml.server.signature";
+ String SAML_SERVER_SIGNATURE_KEYINFO_EXT = "saml.server.signature.keyinfo.ext";
String SAML_FORCE_POST_BINDING = "saml.force.post.binding";
String SAML_ASSERTION_SIGNATURE = "saml.assertion.signature";
String SAML_ENCRYPT = "saml.encrypt";
diff --git a/services/src/main/java/org/keycloak/protocol/saml/SamlProtocol.java b/services/src/main/java/org/keycloak/protocol/saml/SamlProtocol.java
index 7acb155..486633f 100755
--- a/services/src/main/java/org/keycloak/protocol/saml/SamlProtocol.java
+++ b/services/src/main/java/org/keycloak/protocol/saml/SamlProtocol.java
@@ -74,6 +74,7 @@ import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
+import java.util.Objects;
import java.util.Set;
import java.util.UUID;
import org.keycloak.saml.processing.core.util.KeycloakKeySamlExtensionGenerator;
@@ -99,6 +100,7 @@ public class SamlProtocol implements LoginProtocol {
public static final String SAML_REDIRECT_BINDING = "get";
public static final String SAML_REQUEST_ID = "SAML_REQUEST_ID";
public static final String SAML_LOGOUT_BINDING = "saml.logout.binding";
+ public static final String SAML_LOGOUT_ADD_EXTENSIONS_ELEMENT_WITH_KEY_INFO = "saml.logout.addExtensionsElementWithKeyInfo";
public static final String SAML_LOGOUT_REQUEST_ID = "SAML_LOGOUT_REQUEST_ID";
public static final String SAML_LOGOUT_RELAY_STATE = "SAML_LOGOUT_RELAY_STATE";
public static final String SAML_LOGOUT_CANONICALIZATION = "SAML_LOGOUT_CANONICALIZATION";
@@ -379,7 +381,7 @@ public class SamlProtocol implements LoginProtocol {
boolean postBinding = isPostBinding(clientSession);
try {
- if ((! postBinding) && samlClient.requiresRealmSignature()) {
+ if ((! postBinding) && samlClient.requiresRealmSignature() && samlClient.addExtensionsElementWithKeyInfo()) {
builder.addExtension(new KeycloakKeySamlExtensionGenerator(keys.getKid()));
}
@@ -509,7 +511,7 @@ public class SamlProtocol implements LoginProtocol {
logger.debug("frontchannel redirect binding");
String bindingUri = getLogoutServiceUrl(uriInfo, client, SAML_REDIRECT_BINDING);
SAML2LogoutRequestBuilder logoutBuilder = createLogoutRequest(bindingUri, clientSession, client);
- if (samlClient.requiresRealmSignature()) {
+ if (samlClient.requiresRealmSignature() && samlClient.addExtensionsElementWithKeyInfo()) {
KeyManager.ActiveKey keys = session.keys().getActiveKey(realm);
logoutBuilder.addExtension(new KeycloakKeySamlExtensionGenerator(keys.getKid()));
}
@@ -554,7 +556,8 @@ public class SamlProtocol implements LoginProtocol {
}
KeyManager.ActiveKey keys = session.keys().getActiveKey(realm);
binding.signatureAlgorithm(algorithm).signWith(keys.getKid(), keys.getPrivateKey(), keys.getPublicKey(), keys.getCertificate()).signDocument();
- if (! postBinding) { // Only include extension if REDIRECT binding and signing whole SAML protocol message
+ boolean addExtension = (! postBinding) && Objects.equals("true", userSession.getNote(SamlProtocol.SAML_LOGOUT_ADD_EXTENSIONS_ELEMENT_WITH_KEY_INFO));
+ if (addExtension) { // Only include extension if REDIRECT binding and signing whole SAML protocol message
builder.addExtension(new KeycloakKeySamlExtensionGenerator(keys.getKid()));
}
}
diff --git a/services/src/main/java/org/keycloak/protocol/saml/SamlRepresentationAttributes.java b/services/src/main/java/org/keycloak/protocol/saml/SamlRepresentationAttributes.java
index b2b4ee4..a67374a 100755
--- a/services/src/main/java/org/keycloak/protocol/saml/SamlRepresentationAttributes.java
+++ b/services/src/main/java/org/keycloak/protocol/saml/SamlRepresentationAttributes.java
@@ -64,7 +64,11 @@ public class SamlRepresentationAttributes {
public String getSamlServerSignature() {
if (getAttributes() == null) return null;
return getAttributes().get(SamlConfigAttributes.SAML_SERVER_SIGNATURE);
+ }
+ public String getAddExtensionsElementWithKeyInfo() {
+ if (getAttributes() == null) return null;
+ return getAttributes().get(SamlConfigAttributes.SAML_SERVER_SIGNATURE_KEYINFO_EXT);
}
public String getForcePostBinding() {
diff --git a/services/src/main/java/org/keycloak/protocol/saml/SamlService.java b/services/src/main/java/org/keycloak/protocol/saml/SamlService.java
index b3994c1..14c5503 100755
--- a/services/src/main/java/org/keycloak/protocol/saml/SamlService.java
+++ b/services/src/main/java/org/keycloak/protocol/saml/SamlService.java
@@ -347,6 +347,8 @@ public class SamlService extends AuthorizationEndpointBase {
String logoutBinding = getBindingType();
if ("true".equals(samlClient.forcePostBinding()))
logoutBinding = SamlProtocol.SAML_POST_BINDING;
+ boolean postBinding = Objects.equals(SamlProtocol.SAML_POST_BINDING, logoutBinding);
+
String bindingUri = SamlProtocol.getLogoutServiceUrl(uriInfo, client, logoutBinding);
UserSessionModel userSession = authResult.getSession();
userSession.setNote(SamlProtocol.SAML_LOGOUT_BINDING_URI, bindingUri);
@@ -358,6 +360,7 @@ public class SamlService extends AuthorizationEndpointBase {
userSession.setNote(SamlProtocol.SAML_LOGOUT_RELAY_STATE, relayState);
userSession.setNote(SamlProtocol.SAML_LOGOUT_REQUEST_ID, logoutRequest.getID());
userSession.setNote(SamlProtocol.SAML_LOGOUT_BINDING, logoutBinding);
+ userSession.setNote(SamlProtocol.SAML_LOGOUT_ADD_EXTENSIONS_ELEMENT_WITH_KEY_INFO, Boolean.toString((! postBinding) && samlClient.addExtensionsElementWithKeyInfo()));
userSession.setNote(SamlProtocol.SAML_LOGOUT_CANONICALIZATION, samlClient.getCanonicalizationMethod());
userSession.setNote(AuthenticationManager.KEYCLOAK_LOGOUT_PROTOCOL, SamlProtocol.LOGIN_PROTOCOL);
// remove client from logout requests
@@ -413,7 +416,7 @@ public class SamlService extends AuthorizationEndpointBase {
SignatureAlgorithm algorithm = samlClient.getSignatureAlgorithm();
KeyManager.ActiveKey keys = session.keys().getActiveKey(realm);
binding.signatureAlgorithm(algorithm).signWith(keys.getKid(), keys.getPrivateKey(), keys.getPublicKey(), keys.getCertificate()).signDocument();
- if (! postBinding) { // Only include extension if REDIRECT binding and signing whole SAML protocol message
+ if (! postBinding && samlClient.addExtensionsElementWithKeyInfo()) { // Only include extension if REDIRECT binding and signing whole SAML protocol message
builder.addExtension(new KeycloakKeySamlExtensionGenerator(keys.getKid()));
}
}
diff --git a/testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/clients/settings/ClientSettingsForm.java b/testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/clients/settings/ClientSettingsForm.java
index 79092cd..b8cbf4d 100644
--- a/testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/clients/settings/ClientSettingsForm.java
+++ b/testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/clients/settings/ClientSettingsForm.java
@@ -223,6 +223,7 @@ public class ClientSettingsForm extends CreateClientForm {
public static final String SAML_FORCE_POST_BINDING = "saml.force.post.binding";
public static final String SAML_MULTIVALUED_ROLES = "saml.multivalued.roles";
public static final String SAML_SERVER_SIGNATURE = "saml.server.signature";
+ public static final String SAML_SERVER_SIGNATURE_KEYINFO_EXT = "saml.server.signature.keyinfo.ext";
public static final String SAML_SIGNATURE_ALGORITHM = "saml.signature.algorithm";
public static final String SAML_ASSERTION_CONSUMER_URL_POST = "saml_assertion_consumer_url_post";
public static final String SAML_ASSERTION_CONSUMER_URL_REDIRECT = "saml_assertion_consumer_url_redirect";
@@ -236,6 +237,8 @@ public class ClientSettingsForm extends CreateClientForm {
private OnOffSwitch samlAuthnStatement;
@FindBy(xpath = ".//div[@class='onoffswitch' and ./input[@id='samlServerSignature']]")
private OnOffSwitch samlServerSignature;
+ @FindBy(xpath = ".//div[@class='onoffswitch' and ./input[@id='samlServerSignatureEnableKeyInfoExtension']]")
+ private OnOffSwitch samlServerSignatureKeyInfoExt;
@FindBy(xpath = ".//div[@class='onoffswitch' and ./input[@id='samlAssertionSignature']]")
private OnOffSwitch samlAssertionSignature;
@FindBy(id = "signatureAlgorithm")
@@ -277,6 +280,7 @@ public class ClientSettingsForm extends CreateClientForm {
if (samlServerSignature.isOn() || samlAssertionSignature.isOn()) {
signatureAlgorithm.selectByVisibleText(attributes.get(SAML_SIGNATURE_ALGORITHM));
canonicalization.selectByValue("string:" + attributes.get(SAML_SIGNATURE_CANONICALIZATION_METHOD));
+ samlServerSignatureKeyInfoExt.setOn("true".equals(attributes.get(SAML_SERVER_SIGNATURE_KEYINFO_EXT)));
}
samlEncrypt.setOn("true".equals(attributes.get(SAML_ENCRYPT)));
samlClientSignature.setOn("true".equals(attributes.get(SAML_CLIENT_SIGNATURE)));
diff --git a/themes/src/main/resources/theme/base/admin/messages/admin-messages_en.properties b/themes/src/main/resources/theme/base/admin/messages/admin-messages_en.properties
index b330a3c..e892501 100644
--- a/themes/src/main/resources/theme/base/admin/messages/admin-messages_en.properties
+++ b/themes/src/main/resources/theme/base/admin/messages/admin-messages_en.properties
@@ -209,6 +209,8 @@ include-authnstatement=Include AuthnStatement
include-authnstatement.tooltip=Should a statement specifying the method and timestamp be included in login responses?
sign-documents=Sign Documents
sign-documents.tooltip=Should SAML documents be signed by the realm?
+sign-documents-redirect-enable-key-info-ext=Optimize REDIRECT signing key lookup
+sign-documents-redirect-enable-key-info-ext.tooltip=When signing SAML documents in REDIRECT binding for SP that is secured by Keycloak adapter, should the ID of the signing key be included in SAML protocol message in <Extensions> element? This optimizes validation of the signature as the validating party uses a single key instead of trying every known key for validation.
sign-assertions=Sign Assertions
sign-assertions.tooltip=Should assertions inside SAML documents be signed? This setting isn't needed if document is already being signed.
signature-algorithm=Signature Algorithm
diff --git a/themes/src/main/resources/theme/base/admin/resources/js/controllers/clients.js b/themes/src/main/resources/theme/base/admin/resources/js/controllers/clients.js
index 4503b2f..624e9a5 100755
--- a/themes/src/main/resources/theme/base/admin/resources/js/controllers/clients.js
+++ b/themes/src/main/resources/theme/base/admin/resources/js/controllers/clients.js
@@ -860,6 +860,7 @@ module.controller('ClientDetailCtrl', function($scope, realm, client, templates,
$scope.samlAuthnStatement = false;
$scope.samlMultiValuedRoles = false;
$scope.samlServerSignature = false;
+ $scope.samlServerSignatureEnableKeyInfoExtension = false;
$scope.samlAssertionSignature = false;
$scope.samlClientSignature = false;
$scope.samlEncrypt = false;
@@ -908,6 +909,13 @@ module.controller('ClientDetailCtrl', function($scope, realm, client, templates,
}
}
+ if ($scope.client.attributes["saml.server.signature.keyinfo.ext"]) {
+ if ($scope.client.attributes["saml.server.signature.keyinfo.ext"] == "true") {
+ $scope.samlServerSignatureEnableKeyInfoExtension = true;
+ } else {
+ $scope.samlServerSignatureEnableKeyInfoExtension = false;
+ }
+ }
if ($scope.client.attributes["saml.assertion.signature"]) {
if ($scope.client.attributes["saml.assertion.signature"] == "true") {
$scope.samlAssertionSignature = true;
@@ -1115,7 +1123,11 @@ module.controller('ClientDetailCtrl', function($scope, realm, client, templates,
$scope.client.attributes["saml.server.signature"] = "true";
} else {
$scope.client.attributes["saml.server.signature"] = "false";
-
+ }
+ if ($scope.samlServerSignatureEnableKeyInfoExtension == true) {
+ $scope.client.attributes["saml.server.signature.keyinfo.ext"] = "true";
+ } else {
+ $scope.client.attributes["saml.server.signature.keyinfo.ext"] = "false";
}
if ($scope.samlAssertionSignature == true) {
$scope.client.attributes["saml.assertion.signature"] = "true";
diff --git a/themes/src/main/resources/theme/base/admin/resources/partials/client-detail.html b/themes/src/main/resources/theme/base/admin/resources/partials/client-detail.html
index 8601770..6b0f0a6 100755
--- a/themes/src/main/resources/theme/base/admin/resources/partials/client-detail.html
+++ b/themes/src/main/resources/theme/base/admin/resources/partials/client-detail.html
@@ -118,7 +118,7 @@
</div>
</div>
<div class="form-group clearfix block" data-ng-show="protocol == 'saml'">
- <label class="col-md-2 control-label" for="samlServerSignature">{{:: 'include-authnstatement' | translate}}</label>
+ <label class="col-md-2 control-label" for="samlAuthnStatement">{{:: 'include-authnstatement' | translate}}</label>
<div class="col-sm-6">
<input ng-model="samlAuthnStatement" ng-click="switchChange()" name="samlAuthnStatement" id="samlAuthnStatement" onoffswitch on-text="{{:: 'onText' | translate}}" off-text="{{:: 'offText' | translate}}"/>
</div>
@@ -131,6 +131,13 @@
</div>
<kc-tooltip>{{:: 'sign-documents.tooltip' | translate}}</kc-tooltip>
</div>
+ <div class="form-group clearfix block" data-ng-show="protocol == 'saml' && samlServerSignature == true">
+ <label class="col-md-2 control-label" for="samlServerSignatureEnableKeyInfoExtension">{{:: 'sign-documents-redirect-enable-key-info-ext' | translate}}</label>
+ <div class="col-sm-6">
+ <input ng-model="samlServerSignatureEnableKeyInfoExtension" ng-click="switchChange()" name="samlServerSignatureEnableKeyInfoExtension" id="samlServerSignatureEnableKeyInfoExtension" onoffswitch on-text="{{:: 'onText' | translate}}" off-text="{{:: 'offText' | translate}}"/>
+ </div>
+ <kc-tooltip>{{:: 'sign-documents-redirect-enable-key-info-ext.tooltip' | translate}}</kc-tooltip>
+ </div>
<div class="form-group clearfix block" data-ng-show="protocol == 'saml'">
<label class="col-md-2 control-label" for="samlAssertionSignature">{{:: 'sign-assertions' | translate}}</label>
<div class="col-sm-6">