From d5c3bde0af978e82b2402feb3bc2b7ec0cbf0a7b Mon Sep 17 00:00:00 2001 From: Hynek Mlnarik Date: Wed, 2 Nov 2016 08:19:14 +0100 Subject: [PATCH] KEYCLOAK-1881 Make SAML descriptor endpoint return all certificates --- .../common/util/StringPropertyReplacer.java | 2 +- .../keycloak/saml/SPMetadataDescriptor.java | 41 ++++++++++++++----- .../keycloak/protocol/saml/SamlService.java | 41 ++++++++++++++++--- .../main/resources/idp-metadata-template.xml | 14 +------ 4 files changed, 68 insertions(+), 30 deletions(-) diff --git a/common/src/main/java/org/keycloak/common/util/StringPropertyReplacer.java b/common/src/main/java/org/keycloak/common/util/StringPropertyReplacer.java index 3e4839a434..283eb3eaee 100755 --- a/common/src/main/java/org/keycloak/common/util/StringPropertyReplacer.java +++ b/common/src/main/java/org/keycloak/common/util/StringPropertyReplacer.java @@ -98,7 +98,7 @@ public final class StringPropertyReplacer public static String replaceProperties(final String string, final Properties props) { final char[] chars = string.toCharArray(); - StringBuffer buffer = new StringBuffer(); + StringBuilder buffer = new StringBuilder(); boolean properties = false; int state = NORMAL; int start = 0; diff --git a/saml-core/src/main/java/org/keycloak/saml/SPMetadataDescriptor.java b/saml-core/src/main/java/org/keycloak/saml/SPMetadataDescriptor.java index e6c10af640..cc94b8a16a 100755 --- a/saml-core/src/main/java/org/keycloak/saml/SPMetadataDescriptor.java +++ b/saml-core/src/main/java/org/keycloak/saml/SPMetadataDescriptor.java @@ -17,26 +17,21 @@ package org.keycloak.saml; +import org.keycloak.common.util.PemUtils; + /** * @author Bill Burke * @version $Revision: 1 $ */ public class SPMetadataDescriptor { + public static String getSPDescriptor(String binding, String assertionEndpoint, String logoutEndpoint, boolean wantAuthnRequestsSigned, String entityId, String nameIDPolicyFormat, String certificatePem) { String descriptor = "\n" + " \n"; if (wantAuthnRequestsSigned) { - descriptor += - " \n" + - " \n" + - " \n" + - " \n" + certificatePem + "\n" + - " \n" + - " \n" + - " \n" + - " \n"; + descriptor += xmlKeyInfo(null, certificatePem, "signing", true); } descriptor += " \n" + @@ -44,10 +39,34 @@ public class SPMetadataDescriptor { " \n" + " \n"; - descriptor += + " index=\"1\" isDefault=\"true\" />\n" + " \n" + "\n"; return descriptor; } + + public static String xmlKeyInfo(String indentation, String keyId, String pemEncodedCertificate, String purpose, boolean declareDSigNamespace) { + if (pemEncodedCertificate == null) { + return ""; + } + + StringBuilder target = new StringBuilder() + .append(indentation).append("\n") + .append(indentation).append(" \n" : ">\n"); + + if (keyId != null) { + target.append(indentation).append(" ").append(keyId).append("\n"); + } + + target + .append(indentation).append(" \n") + .append(indentation).append(" ").append(pemEncodedCertificate).append("\n") + .append(indentation).append(" \n") + .append(indentation).append(" \n") + .append(indentation).append("\n") + ; + + return target.toString(); + } + } 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 e02093c403..40f615eaec 100755 --- a/services/src/main/java/org/keycloak/protocol/saml/SamlService.java +++ b/services/src/main/java/org/keycloak/protocol/saml/SamlService.java @@ -74,6 +74,17 @@ import java.io.IOException; import java.io.InputStream; import java.net.URI; import java.security.PublicKey; +import java.util.Objects; +import java.util.Properties; +import java.util.Set; +import java.util.TreeSet; +import org.keycloak.common.util.StringPropertyReplacer; +import org.keycloak.dom.saml.v2.metadata.KeyTypes; +import org.keycloak.keys.KeyMetadata; +import org.keycloak.rotation.HardcodedKeyLocator; +import org.keycloak.rotation.KeyLocator; +import org.keycloak.saml.SPMetadataDescriptor; +import org.keycloak.saml.processing.core.util.KeycloakKeySamlExtensionGenerator; /** * Resource class for the oauth/openid connect token service @@ -541,12 +552,30 @@ public class SamlService extends AuthorizationEndpointBase { public static String getIDPMetadataDescriptor(UriInfo uriInfo, KeycloakSession session, RealmModel realm) throws IOException { InputStream is = SamlService.class.getResourceAsStream("/idp-metadata-template.xml"); String template = StreamUtil.readString(is); - template = template.replace("${idp.entityID}", RealmsResource.realmBaseUrl(uriInfo).build(realm.getName()).toString()); - template = template.replace("${idp.sso.HTTP-POST}", RealmsResource.protocolUrl(uriInfo).build(realm.getName(), SamlProtocol.LOGIN_PROTOCOL).toString()); - template = template.replace("${idp.sso.HTTP-Redirect}", RealmsResource.protocolUrl(uriInfo).build(realm.getName(), SamlProtocol.LOGIN_PROTOCOL).toString()); - template = template.replace("${idp.sls.HTTP-POST}", RealmsResource.protocolUrl(uriInfo).build(realm.getName(), SamlProtocol.LOGIN_PROTOCOL).toString()); - template = template.replace("${idp.signing.certificate}", PemUtils.encodeCertificate(session.keys().getActiveKey(realm).getCertificate())); - return template; + Properties props = new Properties(); + props.put("idp.entityID", RealmsResource.realmBaseUrl(uriInfo).build(realm.getName()).toString()); + props.put("idp.sso.HTTP-POST", RealmsResource.protocolUrl(uriInfo).build(realm.getName(), SamlProtocol.LOGIN_PROTOCOL).toString()); + props.put("idp.sso.HTTP-Redirect", RealmsResource.protocolUrl(uriInfo).build(realm.getName(), SamlProtocol.LOGIN_PROTOCOL).toString()); + props.put("idp.sls.HTTP-POST", RealmsResource.protocolUrl(uriInfo).build(realm.getName(), SamlProtocol.LOGIN_PROTOCOL).toString()); + StringBuilder keysString = new StringBuilder(); + Set keys = new TreeSet<>((o1, o2) -> o1.getStatus() == o2.getStatus() // Status can be only PASSIVE OR ACTIVE, push PASSIVE to end of list + ? (int) (o2.getProviderPriority() - o1.getProviderPriority()) + : (o1.getStatus() == KeyMetadata.Status.PASSIVE ? 1 : -1)); + keys.addAll(session.keys().getKeys(realm, false)); + for (KeyMetadata key : keys) { + addKeyInfo(keysString, key, KeyTypes.SIGNING.value()); + } + props.put("idp.signing.certificates", keysString.toString()); + return StringPropertyReplacer.replaceProperties(template, props); + } + + private static void addKeyInfo(StringBuilder target, KeyMetadata key, String purpose) { + if (key == null) { + return; + } + + target.append(SPMetadataDescriptor.xmlKeyInfo(" ", + key.getKid(), PemUtils.encodeCertificate(key.getCertificate()), purpose, false)); } @GET diff --git a/services/src/main/resources/idp-metadata-template.xml b/services/src/main/resources/idp-metadata-template.xml index 0a536478f6..a4416cdaa9 100755 --- a/services/src/main/resources/idp-metadata-template.xml +++ b/services/src/main/resources/idp-metadata-template.xml @@ -16,22 +16,12 @@ ~ limitations under the License. --> - - - - - - - ${idp.signing.certificate} - - - - +${idp.signing.certificates}