Refactor SAML metadata generation to use the SAMLMetadataWriter class

This commit is contained in:
Luca Leonardo Scorcia 2020-04-22 07:38:39 -04:00 committed by Hynek Mlnařík
parent 90cf478f13
commit d6934c64fd
12 changed files with 406 additions and 247 deletions

View file

@ -177,4 +177,4 @@ public enum JBossSAMLURIConstants {
public static JBossSAMLURIConstants from(String key) {
return REVERSE_LOOKUP.from(key);
}
}
}

View file

@ -17,59 +17,111 @@
package org.keycloak.saml;
import org.keycloak.dom.saml.v2.metadata.EndpointType;
import org.keycloak.dom.saml.v2.metadata.EntityDescriptorType;
import org.keycloak.dom.saml.v2.metadata.IndexedEndpointType;
import org.keycloak.dom.saml.v2.metadata.KeyDescriptorType;
import org.keycloak.dom.saml.v2.metadata.KeyTypes;
import org.keycloak.dom.saml.v2.metadata.SPSSODescriptorType;
import java.io.StringWriter;
import java.net.URI;
import java.util.Arrays;
import java.util.List;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamWriter;
import org.keycloak.saml.common.util.StaxUtil;
import org.keycloak.saml.common.exceptions.ProcessingException;
import org.keycloak.saml.processing.core.saml.v2.writers.SAMLMetadataWriter;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import static org.keycloak.saml.common.constants.JBossSAMLURIConstants.XMLDSIG_NSURI;
import static org.keycloak.saml.common.constants.JBossSAMLURIConstants.PROTOCOL_NSURI;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class SPMetadataDescriptor {
public static String getSPDescriptor(String binding, String assertionEndpoint, String logoutEndpoint,
boolean wantAuthnRequestsSigned, boolean wantAssertionsSigned, boolean wantAssertionsEncrypted,
String entityId, String nameIDPolicyFormat, String signingCerts, String encryptionCerts) {
String descriptor =
"<EntityDescriptor xmlns=\"urn:oasis:names:tc:SAML:2.0:metadata\" entityID=\"" + entityId + "\">\n" +
" <SPSSODescriptor AuthnRequestsSigned=\"" + wantAuthnRequestsSigned + "\" WantAssertionsSigned=\"" + wantAssertionsSigned + "\"\n" +
" protocolSupportEnumeration=\"urn:oasis:names:tc:SAML:2.0:protocol urn:oasis:names:tc:SAML:1.1:protocol http://schemas.xmlsoap.org/ws/2003/07/secext\">\n";
public static String getSPDescriptor(URI binding, URI assertionEndpoint, URI logoutEndpoint,
boolean wantAuthnRequestsSigned, boolean wantAssertionsSigned, boolean wantAssertionsEncrypted,
String entityId, String nameIDPolicyFormat, List<Element> signingCerts, List<Element> encryptionCerts)
throws XMLStreamException, ProcessingException, ParserConfigurationException
{
StringWriter sw = new StringWriter();
XMLStreamWriter writer = StaxUtil.getXMLStreamWriter(sw);
SAMLMetadataWriter metadataWriter = new SAMLMetadataWriter(writer);
EntityDescriptorType entityDescriptor = new EntityDescriptorType(entityId);
SPSSODescriptorType spSSODescriptor = new SPSSODescriptorType(Arrays.asList(PROTOCOL_NSURI.get()));
spSSODescriptor.setAuthnRequestsSigned(wantAuthnRequestsSigned);
spSSODescriptor.setWantAssertionsSigned(wantAssertionsSigned);
spSSODescriptor.addNameIDFormat(nameIDPolicyFormat);
spSSODescriptor.addSingleLogoutService(new EndpointType(binding, logoutEndpoint));
if (wantAuthnRequestsSigned && signingCerts != null) {
descriptor += signingCerts;
for (Element key: signingCerts)
{
KeyDescriptorType keyDescriptor = new KeyDescriptorType();
keyDescriptor.setUse(KeyTypes.SIGNING);
keyDescriptor.setKeyInfo(key);
spSSODescriptor.addKeyDescriptor(keyDescriptor);
}
}
if (wantAssertionsEncrypted && encryptionCerts != null) {
descriptor += encryptionCerts;
for (Element key: encryptionCerts)
{
KeyDescriptorType keyDescriptor = new KeyDescriptorType();
keyDescriptor.setUse(KeyTypes.ENCRYPTION);
keyDescriptor.setKeyInfo(key);
spSSODescriptor.addKeyDescriptor(keyDescriptor);
}
}
descriptor +=
" <SingleLogoutService Binding=\"" + binding + "\" Location=\"" + logoutEndpoint + "\"/>\n" +
" <NameIDFormat>" + nameIDPolicyFormat + "\n" +
" </NameIDFormat>\n" +
" <AssertionConsumerService\n" +
" Binding=\"" + binding + "\" Location=\"" + assertionEndpoint + "\"\n" +
" index=\"1\" isDefault=\"true\" />\n" +
" </SPSSODescriptor>\n" +
"</EntityDescriptor>\n";
return descriptor;
IndexedEndpointType assertionConsumerEndpoint = new IndexedEndpointType(binding, assertionEndpoint);
assertionConsumerEndpoint.setIsDefault(true);
assertionConsumerEndpoint.setIndex(1);
spSSODescriptor.addAssertionConsumerService(assertionConsumerEndpoint);
entityDescriptor.addChoiceType(new EntityDescriptorType.EDTChoiceType(Arrays.asList(new EntityDescriptorType.EDTDescriptorChoiceType(spSSODescriptor))));
metadataWriter.writeEntityDescriptor(entityDescriptor);
return sw.toString();
}
public static String xmlKeyInfo(String indentation, String keyId, String pemEncodedCertificate, String purpose, boolean declareDSigNamespace) {
if (pemEncodedCertificate == null) {
return "";
public static Element buildKeyInfoElement(String keyName, String pemEncodedCertificate)
throws javax.xml.parsers.ParserConfigurationException
{
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
DocumentBuilder db = dbf.newDocumentBuilder();
Document doc = db.newDocument();
Element keyInfo = doc.createElementNS(XMLDSIG_NSURI.get(), "ds:KeyInfo");
if (keyName != null) {
Element keyNameElement = doc.createElementNS(XMLDSIG_NSURI.get(), "ds:KeyName");
keyNameElement.setTextContent(keyName);
keyInfo.appendChild(keyNameElement);
}
StringBuilder target = new StringBuilder()
.append(indentation).append("<KeyDescriptor use=\"").append(purpose).append("\">\n")
.append(indentation).append(" <dsig:KeyInfo").append(declareDSigNamespace ? " xmlns:dsig=\"http://www.w3.org/2000/09/xmldsig#\">\n" : ">\n");
Element x509Data = doc.createElementNS(XMLDSIG_NSURI.get(), "ds:X509Data");
if (keyId != null) {
target.append(indentation).append(" <dsig:KeyName>").append(keyId).append("</dsig:KeyName>\n");
}
Element x509Certificate = doc.createElementNS(XMLDSIG_NSURI.get(), "ds:X509Certificate");
x509Certificate.setTextContent(pemEncodedCertificate);
x509Data.appendChild(x509Certificate);
target
.append(indentation).append(" <dsig:X509Data>\n")
.append(indentation).append(" <dsig:X509Certificate>").append(pemEncodedCertificate).append("</dsig:X509Certificate>\n")
.append(indentation).append(" </dsig:X509Data>\n")
.append(indentation).append(" </dsig:KeyInfo>\n")
.append(indentation).append("</KeyDescriptor>\n")
;
keyInfo.appendChild(x509Data);
return target.toString();
return keyInfo;
}
}

View file

@ -187,8 +187,7 @@ public class SAMLMetadataWriter extends BaseWriter {
public void write(SPSSODescriptorType spSSODescriptor) throws ProcessingException {
StaxUtil.writeStartElement(writer, METADATA_PREFIX, JBossSAMLConstants.SP_SSO_DESCRIPTOR.get(), JBossSAMLURIConstants.METADATA_NSURI.get());
StaxUtil.writeAttribute(writer, new QName(JBossSAMLConstants.PROTOCOL_SUPPORT_ENUMERATION.get()), spSSODescriptor
.getProtocolSupportEnumeration().get(0));
writeProtocolSupportEnumeration(spSSODescriptor.getProtocolSupportEnumeration());
// Write the attributes
Boolean authnSigned = spSSODescriptor.isAuthnRequestsSigned();
@ -250,6 +249,12 @@ public class SAMLMetadataWriter extends BaseWriter {
}
writeProtocolSupportEnumeration(idpSSODescriptor.getProtocolSupportEnumeration());
// Get the key descriptors
List<KeyDescriptorType> keyDescriptors = idpSSODescriptor.getKeyDescriptor();
for (KeyDescriptorType keyDescriptor : keyDescriptors) {
writeKeyDescriptor(keyDescriptor);
}
List<IndexedEndpointType> artifactResolutionServices = idpSSODescriptor.getArtifactResolutionService();
for (IndexedEndpointType indexedEndpoint : artifactResolutionServices) {
writeArtifactResolutionService(indexedEndpoint);
@ -260,16 +265,16 @@ public class SAMLMetadataWriter extends BaseWriter {
writeSingleLogoutService(endpoint);
}
List<EndpointType> ssoServices = idpSSODescriptor.getSingleSignOnService();
for (EndpointType endpoint : ssoServices) {
writeSingleSignOnService(endpoint);
}
List<String> nameIDFormats = idpSSODescriptor.getNameIDFormat();
for (String nameIDFormat : nameIDFormats) {
writeNameIDFormat(nameIDFormat);
}
List<EndpointType> ssoServices = idpSSODescriptor.getSingleSignOnService();
for (EndpointType endpoint : ssoServices) {
writeSingleSignOnService(endpoint);
}
List<AttributeType> attributes = idpSSODescriptor.getAttribute();
for (AttributeType attribType : attributes) {
write(attribType);
@ -550,7 +555,10 @@ public class SAMLMetadataWriter extends BaseWriter {
private void writeNameIDFormat(String nameIDFormat) throws ProcessingException {
StaxUtil.writeStartElement(writer, METADATA_PREFIX, JBossSAMLConstants.NAMEID_FORMAT.get(), JBossSAMLURIConstants.METADATA_NSURI.get());
StaxUtil.writeCharacters(writer, nameIDFormat);
if (nameIDFormat != null) {
StaxUtil.writeCharacters(writer, nameIDFormat);
}
StaxUtil.writeEndElement(writer);
}
}

View file

@ -45,12 +45,17 @@ import org.keycloak.saml.processing.core.util.KeycloakKeySamlExtensionGenerator;
import org.keycloak.saml.validators.DestinationValidator;
import org.keycloak.sessions.AuthenticationSessionModel;
import org.w3c.dom.Element;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriBuilder;
import javax.ws.rs.core.UriInfo;
import java.net.URI;
import java.security.KeyPair;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;
@ -230,53 +235,52 @@ public class SAMLIdentityProvider extends AbstractIdentityProvider<SAMLIdentityP
@Override
public Response export(UriInfo uriInfo, RealmModel realm, String format) {
try
{
URI authnBinding = JBossSAMLURIConstants.SAML_HTTP_REDIRECT_BINDING.getUri();
String authnBinding = JBossSAMLURIConstants.SAML_HTTP_REDIRECT_BINDING.get();
if (getConfig().isPostBindingAuthnRequest()) {
authnBinding = JBossSAMLURIConstants.SAML_HTTP_POST_BINDING.get();
}
String endpoint = uriInfo.getBaseUriBuilder()
.path("realms").path(realm.getName())
.path("broker")
.path(getConfig().getAlias())
.path("endpoint")
.build().toString();
boolean wantAuthnRequestsSigned = getConfig().isWantAuthnRequestsSigned();
boolean wantAssertionsSigned = getConfig().isWantAssertionsSigned();
boolean wantAssertionsEncrypted = getConfig().isWantAssertionsEncrypted();
String entityId = getEntityId(uriInfo, realm);
String nameIDPolicyFormat = getConfig().getNameIDPolicyFormat();
StringBuilder signingKeysString = new StringBuilder();
StringBuilder encryptionKeysString = new StringBuilder();
Set<RsaKeyMetadata> 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() == KeyStatus.PASSIVE ? 1 : -1));
keys.addAll(session.keys().getRsaKeys(realm));
for (RsaKeyMetadata key : keys) {
addKeyInfo(signingKeysString, key, KeyTypes.SIGNING.value());
if (key.getStatus() == KeyStatus.ACTIVE) {
addKeyInfo(encryptionKeysString, key, KeyTypes.ENCRYPTION.value());
if (getConfig().isPostBindingAuthnRequest()) {
authnBinding = JBossSAMLURIConstants.SAML_HTTP_POST_BINDING.getUri();
}
URI endpoint = uriInfo.getBaseUriBuilder()
.path("realms").path(realm.getName())
.path("broker")
.path(getConfig().getAlias())
.path("endpoint")
.build();
boolean wantAuthnRequestsSigned = getConfig().isWantAuthnRequestsSigned();
boolean wantAssertionsSigned = getConfig().isWantAssertionsSigned();
boolean wantAssertionsEncrypted = getConfig().isWantAssertionsEncrypted();
String entityId = getEntityId(uriInfo, realm);
String nameIDPolicyFormat = getConfig().getNameIDPolicyFormat();
List<Element> signingKeys = new ArrayList<Element>();
List<Element> encryptionKeys = new ArrayList<Element>();
Set<RsaKeyMetadata> 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() == KeyStatus.PASSIVE ? 1 : -1));
keys.addAll(session.keys().getRsaKeys(realm));
for (RsaKeyMetadata key : keys) {
if (key == null || key.getCertificate() == null) continue;
signingKeys.add(SPMetadataDescriptor.buildKeyInfoElement(key.getKid(), PemUtils.encodeCertificate(key.getCertificate())));
if (key.getStatus() == KeyStatus.ACTIVE)
encryptionKeys.add(SPMetadataDescriptor.buildKeyInfoElement(key.getKid(), PemUtils.encodeCertificate(key.getCertificate())));
}
String descriptor = SPMetadataDescriptor.getSPDescriptor(authnBinding, endpoint, endpoint,
wantAuthnRequestsSigned, wantAssertionsSigned, wantAssertionsEncrypted,
entityId, nameIDPolicyFormat, signingKeys, encryptionKeys);
return Response.ok(descriptor, MediaType.APPLICATION_XML_TYPE).build();
} catch (Exception e) {
logger.warn("Failed to export SAML SP Metadata!", e);
throw new RuntimeException(e);
}
String descriptor = SPMetadataDescriptor.getSPDescriptor(authnBinding, endpoint, endpoint,
wantAuthnRequestsSigned, wantAssertionsSigned, wantAssertionsEncrypted,
entityId, nameIDPolicyFormat, signingKeysString.toString(), encryptionKeysString.toString());
return Response.ok(descriptor, MediaType.APPLICATION_XML_TYPE).build();
}
private static void addKeyInfo(StringBuilder target, RsaKeyMetadata key, String purpose) {
if (key == null) {
return;
}
target.append(SPMetadataDescriptor.xmlKeyInfo(" ", key.getKid(), PemUtils.encodeCertificate(key.getCertificate()), purpose, true));
}
public SignatureAlgorithm getSignatureAlgorithm() {

View file

@ -0,0 +1,130 @@
/*
* Copyright 2016 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.protocol.saml;
import org.keycloak.dom.saml.v2.metadata.EndpointType;
import org.keycloak.dom.saml.v2.metadata.EntitiesDescriptorType;
import org.keycloak.dom.saml.v2.metadata.EntityDescriptorType;
import org.keycloak.dom.saml.v2.metadata.IDPSSODescriptorType;
import org.keycloak.dom.saml.v2.metadata.KeyDescriptorType;
import org.keycloak.dom.saml.v2.metadata.KeyTypes;
import java.io.StringWriter;
import java.net.URI;
import java.util.Arrays;
import java.util.List;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamWriter;
import org.keycloak.saml.common.exceptions.ProcessingException;
import org.keycloak.saml.processing.core.saml.v2.writers.SAMLMetadataWriter;
import org.keycloak.saml.common.util.StaxUtil;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import static org.keycloak.saml.common.constants.JBossSAMLURIConstants.SAML_HTTP_POST_BINDING;
import static org.keycloak.saml.common.constants.JBossSAMLURIConstants.SAML_HTTP_REDIRECT_BINDING;
import static org.keycloak.saml.common.constants.JBossSAMLURIConstants.SAML_SOAP_BINDING;
import static org.keycloak.saml.common.constants.JBossSAMLURIConstants.NAMEID_FORMAT_PERSISTENT;
import static org.keycloak.saml.common.constants.JBossSAMLURIConstants.NAMEID_FORMAT_TRANSIENT;
import static org.keycloak.saml.common.constants.JBossSAMLURIConstants.NAMEID_FORMAT_UNSPECIFIED;
import static org.keycloak.saml.common.constants.JBossSAMLURIConstants.NAMEID_FORMAT_EMAIL;
import static org.keycloak.saml.common.constants.JBossSAMLURIConstants.XMLDSIG_NSURI;
import static org.keycloak.saml.common.constants.JBossSAMLURIConstants.PROTOCOL_NSURI;
/**
* @version $Revision: 1 $
*/
public class IDPMetadataDescriptor {
public static String getIDPDescriptor(URI loginPostEndpoint, URI loginRedirectEndpoint, URI logoutEndpoint,
String entityId, boolean wantAuthnRequestsSigned, List<Element> signingCerts, List<Element> encryptionCerts)
throws XMLStreamException, ProcessingException, ParserConfigurationException
{
StringWriter sw = new StringWriter();
XMLStreamWriter writer = StaxUtil.getXMLStreamWriter(sw);
SAMLMetadataWriter metadataWriter = new SAMLMetadataWriter(writer);
EntitiesDescriptorType entitiesDescriptor = new EntitiesDescriptorType();
entitiesDescriptor.setName("urn:keycloak");
EntityDescriptorType entityDescriptor = new EntityDescriptorType(entityId);
IDPSSODescriptorType spIDPDescriptor = new IDPSSODescriptorType(Arrays.asList(PROTOCOL_NSURI.get()));
spIDPDescriptor.setWantAuthnRequestsSigned(wantAuthnRequestsSigned);
spIDPDescriptor.addNameIDFormat(NAMEID_FORMAT_PERSISTENT.get());
spIDPDescriptor.addNameIDFormat(NAMEID_FORMAT_TRANSIENT.get());
spIDPDescriptor.addNameIDFormat(NAMEID_FORMAT_UNSPECIFIED.get());
spIDPDescriptor.addNameIDFormat(NAMEID_FORMAT_EMAIL.get());
spIDPDescriptor.addSingleLogoutService(new EndpointType(SAML_HTTP_POST_BINDING.getUri(), logoutEndpoint));
spIDPDescriptor.addSingleLogoutService(new EndpointType(SAML_HTTP_REDIRECT_BINDING.getUri(), logoutEndpoint));
spIDPDescriptor.addSingleSignOnService(new EndpointType(SAML_HTTP_POST_BINDING.getUri(), loginPostEndpoint));
spIDPDescriptor.addSingleSignOnService(new EndpointType(SAML_HTTP_REDIRECT_BINDING.getUri(), loginRedirectEndpoint));
spIDPDescriptor.addSingleSignOnService(new EndpointType(SAML_SOAP_BINDING.getUri(), loginPostEndpoint));
if (wantAuthnRequestsSigned && signingCerts != null) {
for (Element key: signingCerts)
{
KeyDescriptorType keyDescriptor = new KeyDescriptorType();
keyDescriptor.setUse(KeyTypes.SIGNING);
keyDescriptor.setKeyInfo(key);
spIDPDescriptor.addKeyDescriptor(keyDescriptor);
}
}
entityDescriptor.addChoiceType(new EntityDescriptorType.EDTChoiceType(Arrays.asList(new EntityDescriptorType.EDTDescriptorChoiceType(spIDPDescriptor))));
entitiesDescriptor.addEntityDescriptor(entityDescriptor);
metadataWriter.writeEntitiesDescriptor(entitiesDescriptor);
return sw.toString();
}
public static Element buildKeyInfoElement(String keyName, String pemEncodedCertificate)
throws javax.xml.parsers.ParserConfigurationException
{
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
DocumentBuilder db = dbf.newDocumentBuilder();
Document doc = db.newDocument();
Element keyInfo = doc.createElementNS(XMLDSIG_NSURI.get(), "ds:KeyInfo");
if (keyName != null) {
Element keyNameElement = doc.createElementNS(XMLDSIG_NSURI.get(), "ds:KeyName");
keyNameElement.setTextContent(keyName);
keyInfo.appendChild(keyNameElement);
}
Element x509Data = doc.createElementNS(XMLDSIG_NSURI.get(), "ds:X509Data");
Element x509Certificate = doc.createElementNS(XMLDSIG_NSURI.get(), "ds:X509Certificate");
x509Certificate.setTextContent(pemEncodedCertificate);
x509Data.appendChild(x509Certificate);
keyInfo.appendChild(x509Data);
return keyInfo;
}
}

View file

@ -63,6 +63,8 @@ import org.keycloak.services.resources.RealmsResource;
import org.keycloak.services.util.CacheControlUtil;
import org.keycloak.utils.MediaType;
import org.w3c.dom.Element;
import javax.ws.rs.Consumes;
import javax.ws.rs.FormParam;
import javax.ws.rs.GET;
@ -77,7 +79,9 @@ import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.security.PublicKey;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Objects;
import java.util.Properties;
import java.util.Set;
@ -656,38 +660,28 @@ public class SamlService extends AuthorizationEndpointBase {
}
public static String getIDPMetadataDescriptor(UriInfo uriInfo, KeycloakSession session, RealmModel realm) {
InputStream is = SamlService.class.getResourceAsStream("/idp-metadata-template.xml");
String template;
try {
template = StreamUtil.readString(is, StandardCharsets.UTF_8);
} catch (IOException ex) {
logger.error("Cannot generate IdP metadata", ex);
return "";
}
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<KeyWrapper> 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() == KeyStatus.PASSIVE ? 1 : -1));
keys.addAll(session.keys().getKeys(realm, KeyUse.SIG, Algorithm.RS256));
for (KeyWrapper 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, KeyWrapper key, String purpose) {
if (key == null) {
return;
}
try {
List<Element> signingKeys = new ArrayList<Element>();
for (KeyWrapper key : keys) {
signingKeys.add(IDPMetadataDescriptor.buildKeyInfoElement(key.getKid(), PemUtils.encodeCertificate(key.getCertificate())));
}
target.append(SPMetadataDescriptor.xmlKeyInfo(" ",
key.getKid(), PemUtils.encodeCertificate(key.getCertificate()), purpose, false));
return IDPMetadataDescriptor.getIDPDescriptor(
RealmsResource.protocolUrl(uriInfo).build(realm.getName(), SamlProtocol.LOGIN_PROTOCOL),
RealmsResource.protocolUrl(uriInfo).build(realm.getName(), SamlProtocol.LOGIN_PROTOCOL),
RealmsResource.protocolUrl(uriInfo).build(realm.getName(), SamlProtocol.LOGIN_PROTOCOL),
RealmsResource.realmBaseUrl(uriInfo).build(realm.getName()).toString(),
true,
signingKeys, null);
} catch (Exception ex) {
logger.error("Cannot generate IdP metadata", ex);
return "";
}
}
private boolean isClientProtocolCorrect(ClientModel clientModel) {

View file

@ -17,7 +17,9 @@
package org.keycloak.protocol.saml.installation;
import org.jboss.logging.Logger;
import org.keycloak.Config;
import org.keycloak.dom.saml.v2.metadata.KeyTypes;
import org.keycloak.models.ClientModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakSessionFactory;
@ -28,10 +30,13 @@ import org.keycloak.protocol.saml.SamlProtocol;
import org.keycloak.saml.SPMetadataDescriptor;
import org.keycloak.saml.common.constants.JBossSAMLURIConstants;
import org.w3c.dom.Element;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import java.net.URI;
import org.keycloak.dom.saml.v2.metadata.KeyTypes;
import java.util.Arrays;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
@ -39,34 +44,41 @@ import org.keycloak.dom.saml.v2.metadata.KeyTypes;
*/
public class SamlSPDescriptorClientInstallation implements ClientInstallationProvider {
protected static final Logger logger = Logger.getLogger(SamlSPDescriptorClientInstallation.class);
public static final String SAML_CLIENT_INSTALATION_SP_DESCRIPTOR = "saml-sp-descriptor";
private static final String FALLBACK_ERROR_URL_STRING = "ERROR:ENDPOINT NOT SET";
private static final String FALLBACK_ERROR_URL_STRING = "ERROR:ENDPOINT_NOT_SET";
public static String getSPDescriptorForClient(ClientModel client) {
SamlClient samlClient = new SamlClient(client);
String assertionUrl;
String logoutUrl;
String binding;
if (samlClient.forcePostBinding()) {
assertionUrl = client.getAttribute(SamlProtocol.SAML_ASSERTION_CONSUMER_URL_POST_ATTRIBUTE);
logoutUrl = client.getAttribute(SamlProtocol.SAML_SINGLE_LOGOUT_SERVICE_URL_POST_ATTRIBUTE);
binding = JBossSAMLURIConstants.SAML_HTTP_POST_BINDING.get();
} else { //redirect binding
assertionUrl = client.getAttribute(SamlProtocol.SAML_ASSERTION_CONSUMER_URL_REDIRECT_ATTRIBUTE);
logoutUrl = client.getAttribute(SamlProtocol.SAML_SINGLE_LOGOUT_SERVICE_URL_REDIRECT_ATTRIBUTE);
binding = JBossSAMLURIConstants.SAML_HTTP_REDIRECT_BINDING.get();
try {
SamlClient samlClient = new SamlClient(client);
String assertionUrl;
String logoutUrl;
URI binding;
if (samlClient.forcePostBinding()) {
assertionUrl = client.getAttribute(SamlProtocol.SAML_ASSERTION_CONSUMER_URL_POST_ATTRIBUTE);
logoutUrl = client.getAttribute(SamlProtocol.SAML_SINGLE_LOGOUT_SERVICE_URL_POST_ATTRIBUTE);
binding = JBossSAMLURIConstants.SAML_HTTP_POST_BINDING.getUri();
} else { //redirect binding
assertionUrl = client.getAttribute(SamlProtocol.SAML_ASSERTION_CONSUMER_URL_REDIRECT_ATTRIBUTE);
logoutUrl = client.getAttribute(SamlProtocol.SAML_SINGLE_LOGOUT_SERVICE_URL_REDIRECT_ATTRIBUTE);
binding = JBossSAMLURIConstants.SAML_HTTP_REDIRECT_BINDING.getUri();
}
if (assertionUrl == null || assertionUrl.trim().isEmpty()) assertionUrl = client.getManagementUrl();
if (assertionUrl == null || assertionUrl.trim().isEmpty()) assertionUrl = FALLBACK_ERROR_URL_STRING;
if (logoutUrl == null || logoutUrl.trim().isEmpty()) logoutUrl = client.getManagementUrl();
if (logoutUrl == null || logoutUrl.trim().isEmpty()) logoutUrl = FALLBACK_ERROR_URL_STRING;
String nameIdFormat = samlClient.getNameIDFormat();
if (nameIdFormat == null) nameIdFormat = SamlProtocol.SAML_DEFAULT_NAMEID_FORMAT;
Element spCertificate = SPMetadataDescriptor.buildKeyInfoElement(null, samlClient.getClientSigningCertificate());
Element encCertificate = SPMetadataDescriptor.buildKeyInfoElement(null, samlClient.getClientEncryptingCertificate());
return SPMetadataDescriptor.getSPDescriptor(binding, new URI(assertionUrl), new URI(logoutUrl), samlClient.requiresClientSignature(),
samlClient.requiresAssertionSignature(), samlClient.requiresEncryption(),
client.getClientId(), nameIdFormat, Arrays.asList(spCertificate), Arrays.asList(encCertificate));
} catch (Exception ex) {
logger.error("Cannot generate SP metadata", ex);
return "";
}
if (assertionUrl == null || assertionUrl.trim().isEmpty()) assertionUrl = client.getManagementUrl();
if (assertionUrl == null || assertionUrl.trim().isEmpty()) assertionUrl = FALLBACK_ERROR_URL_STRING;
if (logoutUrl == null || logoutUrl.trim().isEmpty()) logoutUrl = client.getManagementUrl();
if (logoutUrl == null || logoutUrl.trim().isEmpty()) logoutUrl = FALLBACK_ERROR_URL_STRING;
String nameIdFormat = samlClient.getNameIDFormat();
if (nameIdFormat == null) nameIdFormat = SamlProtocol.SAML_DEFAULT_NAMEID_FORMAT;
String spCertificate = SPMetadataDescriptor.xmlKeyInfo(" ", null, samlClient.getClientSigningCertificate(), KeyTypes.SIGNING.value(), true);
String encCertificate = SPMetadataDescriptor.xmlKeyInfo(" ", null, samlClient.getClientEncryptingCertificate(), KeyTypes.ENCRYPTION.value(), true);
return SPMetadataDescriptor.getSPDescriptor(binding, assertionUrl, logoutUrl, samlClient.requiresClientSignature(),
samlClient.requiresAssertionSignature(), samlClient.requiresEncryption(),
client.getClientId(), nameIdFormat, spCertificate, encCertificate);
}
@Override

View file

@ -1,45 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Copyright 2016 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.
-->
<EntitiesDescriptor Name="urn:keycloak" xmlns="urn:oasis:names:tc:SAML:2.0:metadata"
xmlns:dsig="http://www.w3.org/2000/09/xmldsig#">
<EntityDescriptor entityID="${idp.entityID}">
<IDPSSODescriptor WantAuthnRequestsSigned="true"
protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol">
${idp.signing.certificates}
<SingleLogoutService
Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"
Location="${idp.sls.HTTP-POST}" />
<SingleLogoutService
Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect"
Location="${idp.sso.HTTP-Redirect}" />
<NameIDFormat>urn:oasis:names:tc:SAML:2.0:nameid-format:persistent</NameIDFormat>
<NameIDFormat>urn:oasis:names:tc:SAML:2.0:nameid-format:transient</NameIDFormat>
<NameIDFormat>urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified</NameIDFormat>
<NameIDFormat>urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress</NameIDFormat>
<SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"
Location="${idp.sso.HTTP-POST}" />
<SingleSignOnService
Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect"
Location="${idp.sso.HTTP-Redirect}" />
<SingleSignOnService
Binding="urn:oasis:names:tc:SAML:2.0:bindings:SOAP"
Location="${idp.sso.HTTP-POST}" />
</IDPSSODescriptor>
</EntityDescriptor>
</EntitiesDescriptor>

View file

@ -933,9 +933,7 @@ public class IdentityProviderTest extends AbstractAdminTest {
Assert.assertTrue("AuthnRequestsSigned", desc.isAuthnRequestsSigned());
Set<String> expected = new HashSet<>(Arrays.asList(
"urn:oasis:names:tc:SAML:2.0:protocol",
"urn:oasis:names:tc:SAML:1.1:protocol",
"http://schemas.xmlsoap.org/ws/2003/07/secext"));
"urn:oasis:names:tc:SAML:2.0:protocol"));
Set<String> actual = new HashSet<>(desc.getProtocolSupportEnumeration());

View file

@ -48,6 +48,8 @@ import static org.junit.Assert.assertThat;
import static org.hamcrest.Matchers.*;
import static org.keycloak.testsuite.util.ServerURLs.getAuthServerContextRoot;
import static org.keycloak.saml.common.constants.JBossSAMLURIConstants.METADATA_NSURI;
/**
* Test getting the installation/configuration files for OIDC and SAML.
*
@ -189,10 +191,11 @@ public class InstallationTest extends AbstractClientTest {
}
@Test
public void testSamlMetadataSpDescriptor() {
public void testSamlMetadataSpDescriptor() throws Exception {
String xml = samlClient.getInstallationProvider(SamlSPDescriptorClientInstallation.SAML_CLIENT_INSTALATION_SP_DESCRIPTOR);
assertThat(xml, containsString("<EntityDescriptor"));
assertThat(xml, containsString("<SPSSODescriptor"));
Document doc = getDocumentFromXmlString(xml);
assertElements(doc, METADATA_NSURI.get(), "EntityDescriptor", null);
assertElements(doc, METADATA_NSURI.get(), "SPSSODescriptor", null);
assertThat(xml, containsString(SAML_NAME));
}
@ -215,9 +218,9 @@ public class InstallationTest extends AbstractClientTest {
Document doc = getDocumentFromXmlString(updater.getResource().getInstallationProvider(SamlSPDescriptorClientInstallation.SAML_CLIENT_INSTALATION_SP_DESCRIPTOR));
Map<String, String> attrNamesAndValues = new HashMap<>();
attrNamesAndValues.put("Binding", JBossSAMLURIConstants.SAML_HTTP_POST_BINDING.get());
attrNamesAndValues.put("Location", "ERROR:ENDPOINT NOT SET");
assertElements(doc, "SingleLogoutService", attrNamesAndValues);
assertElements(doc, "AssertionConsumerService", attrNamesAndValues);
attrNamesAndValues.put("Location", "ERROR:ENDPOINT_NOT_SET");
assertElements(doc, METADATA_NSURI.get(), "SingleLogoutService", attrNamesAndValues);
assertElements(doc, METADATA_NSURI.get(), "AssertionConsumerService", attrNamesAndValues);
attrNamesAndValues.clear();
//fallback to adminUrl
@ -226,8 +229,8 @@ public class InstallationTest extends AbstractClientTest {
doc = getDocumentFromXmlString(updater.getResource().getInstallationProvider(SamlSPDescriptorClientInstallation.SAML_CLIENT_INSTALATION_SP_DESCRIPTOR));
attrNamesAndValues.put("Binding", JBossSAMLURIConstants.SAML_HTTP_POST_BINDING.get());
attrNamesAndValues.put("Location", "admin-url");
assertElements(doc, "SingleLogoutService", attrNamesAndValues);
assertElements(doc, "AssertionConsumerService", attrNamesAndValues);
assertElements(doc, METADATA_NSURI.get(), "SingleLogoutService", attrNamesAndValues);
assertElements(doc, METADATA_NSURI.get(), "AssertionConsumerService", attrNamesAndValues);
attrNamesAndValues.clear();
//fine grained
@ -241,11 +244,11 @@ public class InstallationTest extends AbstractClientTest {
doc = getDocumentFromXmlString(updater.getResource().getInstallationProvider(SamlSPDescriptorClientInstallation.SAML_CLIENT_INSTALATION_SP_DESCRIPTOR));
attrNamesAndValues.put("Binding", JBossSAMLURIConstants.SAML_HTTP_POST_BINDING.get());
attrNamesAndValues.put("Location", "saml-logout-post-url");
assertElements(doc, "SingleLogoutService", attrNamesAndValues);
assertElements(doc, METADATA_NSURI.get(), "SingleLogoutService", attrNamesAndValues);
attrNamesAndValues.clear();
attrNamesAndValues.put("Binding", JBossSAMLURIConstants.SAML_HTTP_POST_BINDING.get());
attrNamesAndValues.put("Location", "saml-assertion-post-url");
assertElements(doc, "AssertionConsumerService", attrNamesAndValues);
assertElements(doc, METADATA_NSURI.get(), "AssertionConsumerService", attrNamesAndValues);
}
assertAdminEvents.assertEvent(getRealmId(), OperationType.UPDATE, AdminEventPaths.clientResourcePath(samlClientId), ResourceType.CLIENT);
}
@ -263,9 +266,9 @@ public class InstallationTest extends AbstractClientTest {
Document doc = getDocumentFromXmlString(updater.getResource().getInstallationProvider(SamlSPDescriptorClientInstallation.SAML_CLIENT_INSTALATION_SP_DESCRIPTOR));
Map<String, String> attrNamesAndValues = new HashMap<>();
attrNamesAndValues.put("Binding", JBossSAMLURIConstants.SAML_HTTP_REDIRECT_BINDING.get());
attrNamesAndValues.put("Location", "ERROR:ENDPOINT NOT SET");
assertElements(doc, "SingleLogoutService", attrNamesAndValues);
assertElements(doc, "AssertionConsumerService", attrNamesAndValues);
attrNamesAndValues.put("Location", "ERROR:ENDPOINT_NOT_SET");
assertElements(doc, METADATA_NSURI.get(), "SingleLogoutService", attrNamesAndValues);
assertElements(doc, METADATA_NSURI.get(), "AssertionConsumerService", attrNamesAndValues);
attrNamesAndValues.clear();
//fallback to adminUrl
@ -274,8 +277,8 @@ public class InstallationTest extends AbstractClientTest {
doc = getDocumentFromXmlString(updater.getResource().getInstallationProvider(SamlSPDescriptorClientInstallation.SAML_CLIENT_INSTALATION_SP_DESCRIPTOR));
attrNamesAndValues.put("Binding", JBossSAMLURIConstants.SAML_HTTP_REDIRECT_BINDING.get());
attrNamesAndValues.put("Location", "admin-url");
assertElements(doc, "SingleLogoutService", attrNamesAndValues);
assertElements(doc, "AssertionConsumerService", attrNamesAndValues);
assertElements(doc, METADATA_NSURI.get(), "SingleLogoutService", attrNamesAndValues);
assertElements(doc, METADATA_NSURI.get(), "AssertionConsumerService", attrNamesAndValues);
attrNamesAndValues.clear();
//fine grained
@ -288,29 +291,33 @@ public class InstallationTest extends AbstractClientTest {
doc = getDocumentFromXmlString(updater.getResource().getInstallationProvider(SamlSPDescriptorClientInstallation.SAML_CLIENT_INSTALATION_SP_DESCRIPTOR));
attrNamesAndValues.put("Binding", JBossSAMLURIConstants.SAML_HTTP_REDIRECT_BINDING.get());
attrNamesAndValues.put("Location", "saml-logout-redirect-url");
assertElements(doc, "SingleLogoutService", attrNamesAndValues);
assertElements(doc, METADATA_NSURI.get(), "SingleLogoutService", attrNamesAndValues);
attrNamesAndValues.clear();
attrNamesAndValues.put("Binding", JBossSAMLURIConstants.SAML_HTTP_REDIRECT_BINDING.get());
attrNamesAndValues.put("Location", "saml-assertion-redirect-url");
assertElements(doc, "AssertionConsumerService", attrNamesAndValues);
assertElements(doc, METADATA_NSURI.get(), "AssertionConsumerService", attrNamesAndValues);
}
assertAdminEvents.assertEvent(getRealmId(), OperationType.UPDATE, AdminEventPaths.clientResourcePath(samlClientId), ResourceType.CLIENT);
}
private Document getDocumentFromXmlString(String xml) throws SAXException, ParserConfigurationException, IOException {
DocumentBuilder db = DocumentBuilderFactory.newInstance().newDocumentBuilder();
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.setNamespaceAware(true);
DocumentBuilder db = dbf.newDocumentBuilder();
InputSource is = new InputSource();
is.setCharacterStream(new StringReader(xml));
return db.parse(is);
}
private void assertElements(Document doc, String tagName, Map<String, String> attrNamesAndValues) {
NodeList elementsByTagName = doc.getElementsByTagName(tagName);
private void assertElements(Document doc, String tagNamespace, String tagName, Map<String, String> attrNamesAndValues) {
NodeList elementsByTagName = doc.getElementsByTagNameNS(tagNamespace, tagName);
assertThat("Expected exactly one " + tagName + " element!", elementsByTagName.getLength(), is(equalTo(1)));
Node element = elementsByTagName.item(0);
for (String attrName : attrNamesAndValues.keySet()) {
assertThat(element.getAttributes().getNamedItem(attrName).getNodeValue(), containsString(attrNamesAndValues.get(attrName)));
if (attrNamesAndValues != null) {
for (String attrName : attrNamesAndValues.keySet()) {
assertThat(element.getAttributes().getNamedItem(attrName).getNodeValue(), containsString(attrNamesAndValues.get(attrName)));
}
}
}
}

View file

@ -5,35 +5,35 @@
>
<IDPSSODescriptor WantAuthnRequestsSigned="true"
protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol">
<NameIDFormat>urn:oasis:names:tc:SAML:2.0:nameid-format:persistent</NameIDFormat>
<NameIDFormat>urn:oasis:names:tc:SAML:2.0:nameid-format:transient</NameIDFormat>
<NameIDFormat>urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified</NameIDFormat>
<NameIDFormat>urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress</NameIDFormat>
<SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"
Location="http://localhost:8080/auth/realms/master/protocol/saml" />
<KeyDescriptor use="signing">
<dsig:KeyInfo>
<dsig:KeyName>hAoy_sBtpu6FdRVCk7ykihF6Ug-o0pKPK3LN9RYkeqs</dsig:KeyName>
<dsig:X509Data>
<dsig:X509Certificate>
MIICmzCCAYMCBgFUYnC0OjANBgkqhkiG9w0BAQsFADARMQ8wDQYDVQQDDAZtYXN0ZXIwHhcNMTYwNDI5MTQzMjEzWhcNMjYwNDI5MTQzMzUzWjARMQ8wDQYDVQQDDAZtYXN0ZXIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCN25AW1poMEZRbuMAHG58AThZmCwMV6/Gcui4mjGacRFyudgqzLjQ2rxpoW41JAtLjbjeAhuWvirUcFVcOeS3gM/ZC27qCpYighAcylZz6MYocnEe1+e8rPPk4JlID6Wv62dgu+pL/vYsQpRhvD3Y2c/ytgr5D32xF+KnzDehUy5BSyzypvu12Wq9mS5vK5tzkN37EjkhpY2ZxaXPubjDIITCAL4Q8M/m5IlacBaUZbzI4AQrHnMP1O1IH2dHSWuMiBe+xSDTco72PmuYPJKTV4wQdeBUIkYbfLc4RxVmXEvgkQgyW86EoMPxlWJpj7+mTIR+l+2thZPr/VgwTs82rAgMBAAEwDQYJKoZIhvcNAQELBQADggEBAA/Ip/Hi8RoVu5ouaFFlc5whT7ltuK8slfLGW4tM4vJXhInYwsqIRQKBNDYW/64xle3eII4u1yAH1OYRRwEs7Em1pr4QuFuTY1at+aE0sE46XDlyESI0txJjWxYoT133vM0We2pj1b2nxgU30rwjKA3whnKEfTEYT/n3JBSqNggy6l8ZGw/oPSgvPaR4+xeB1tfQFC4VrLoYKoqH6hAL530nKxL+qV8AIfL64NDEE8ankIAEDAAFe8x3CPUfXR/p4KOANKkpz8ieQaHDb1eITkAwUwjESj6UF9D1aePlhWls/HX0gujFXtWfWfrJ8CU/ogwlH8y1jgRuLjFQYZk6llc=
</dsig:X509Certificate>
</dsig:X509Data>
</dsig:KeyInfo>
</KeyDescriptor>
<KeyDescriptor use="signing">
<dsig:KeyInfo>
<dsig:KeyName>FJ86GcF3jTbNLOco4NvZkUCIUmfYCqoqtOQeMfbhNlE</dsig:KeyName>
<dsig:X509Data>
<dsig:X509Certificate>
MIIBnDCCAQUCBgFYKXKsPTANBgkqhkiG9w0BAQsFADAUMRIwEAYDVQQDDAlzYW1sLWRlbW8wHhcNMTYxMTAzMDkwNzEwWhcNMjYxMTAzMDkwODUwWjAUMRIwEAYDVQQDDAlzYW1sLWRlbW8wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAKtWsK5O0CtuBpnMvWG+HTG0vmZzujQ2o9WdheQu+BzCILcGMsbDW0YQaglpcO5JpGWWhubnckGGPHfdQ2/7nP9QwbiTK0FbGF41UqcvoaCqU1psxoV88s8IXyQCAqeyLv00yj6foqdJjxh5SZ5z+na+M7Y2OxIBVxYRAxWEnfUvAgMBAAEwDQYJKoZIhvcNAQELBQADgYEAhetvOU8TyqfZF5jpv0IcrviLl/DoFrbjByeHR+pu/vClcAOjL/u7oQELuuTfNsBI4tpexUj5G8q/YbEz0gk7idfLXrAUVcsR73oTngrhRfwUSmPrjjK0kjcRb6HL9V/+wh3R/6mEd59U08ExT8N38rhmn0CI3ehMdebReprP7U8=
</dsig:X509Certificate>
</dsig:X509Data>
</dsig:KeyInfo>
</KeyDescriptor>
<SingleLogoutService
Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"
Location="http://localhost:8080/auth/realms/master/protocol/saml" />
<KeyDescriptor use="signing">
<dsig:KeyInfo>
<dsig:KeyName>hAoy_sBtpu6FdRVCk7ykihF6Ug-o0pKPK3LN9RYkeqs</dsig:KeyName>
<dsig:X509Data>
<dsig:X509Certificate>
MIICmzCCAYMCBgFUYnC0OjANBgkqhkiG9w0BAQsFADARMQ8wDQYDVQQDDAZtYXN0ZXIwHhcNMTYwNDI5MTQzMjEzWhcNMjYwNDI5MTQzMzUzWjARMQ8wDQYDVQQDDAZtYXN0ZXIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCN25AW1poMEZRbuMAHG58AThZmCwMV6/Gcui4mjGacRFyudgqzLjQ2rxpoW41JAtLjbjeAhuWvirUcFVcOeS3gM/ZC27qCpYighAcylZz6MYocnEe1+e8rPPk4JlID6Wv62dgu+pL/vYsQpRhvD3Y2c/ytgr5D32xF+KnzDehUy5BSyzypvu12Wq9mS5vK5tzkN37EjkhpY2ZxaXPubjDIITCAL4Q8M/m5IlacBaUZbzI4AQrHnMP1O1IH2dHSWuMiBe+xSDTco72PmuYPJKTV4wQdeBUIkYbfLc4RxVmXEvgkQgyW86EoMPxlWJpj7+mTIR+l+2thZPr/VgwTs82rAgMBAAEwDQYJKoZIhvcNAQELBQADggEBAA/Ip/Hi8RoVu5ouaFFlc5whT7ltuK8slfLGW4tM4vJXhInYwsqIRQKBNDYW/64xle3eII4u1yAH1OYRRwEs7Em1pr4QuFuTY1at+aE0sE46XDlyESI0txJjWxYoT133vM0We2pj1b2nxgU30rwjKA3whnKEfTEYT/n3JBSqNggy6l8ZGw/oPSgvPaR4+xeB1tfQFC4VrLoYKoqH6hAL530nKxL+qV8AIfL64NDEE8ankIAEDAAFe8x3CPUfXR/p4KOANKkpz8ieQaHDb1eITkAwUwjESj6UF9D1aePlhWls/HX0gujFXtWfWfrJ8CU/ogwlH8y1jgRuLjFQYZk6llc=
</dsig:X509Certificate>
</dsig:X509Data>
</dsig:KeyInfo>
</KeyDescriptor>
<KeyDescriptor use="signing">
<dsig:KeyInfo>
<dsig:KeyName>FJ86GcF3jTbNLOco4NvZkUCIUmfYCqoqtOQeMfbhNlE</dsig:KeyName>
<dsig:X509Data>
<dsig:X509Certificate>
MIIBnDCCAQUCBgFYKXKsPTANBgkqhkiG9w0BAQsFADAUMRIwEAYDVQQDDAlzYW1sLWRlbW8wHhcNMTYxMTAzMDkwNzEwWhcNMjYxMTAzMDkwODUwWjAUMRIwEAYDVQQDDAlzYW1sLWRlbW8wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAKtWsK5O0CtuBpnMvWG+HTG0vmZzujQ2o9WdheQu+BzCILcGMsbDW0YQaglpcO5JpGWWhubnckGGPHfdQ2/7nP9QwbiTK0FbGF41UqcvoaCqU1psxoV88s8IXyQCAqeyLv00yj6foqdJjxh5SZ5z+na+M7Y2OxIBVxYRAxWEnfUvAgMBAAEwDQYJKoZIhvcNAQELBQADgYEAhetvOU8TyqfZF5jpv0IcrviLl/DoFrbjByeHR+pu/vClcAOjL/u7oQELuuTfNsBI4tpexUj5G8q/YbEz0gk7idfLXrAUVcsR73oTngrhRfwUSmPrjjK0kjcRb6HL9V/+wh3R/6mEd59U08ExT8N38rhmn0CI3ehMdebReprP7U8=
</dsig:X509Certificate>
</dsig:X509Data>
</dsig:KeyInfo>
</KeyDescriptor>
<NameIDFormat>urn:oasis:names:tc:SAML:2.0:nameid-format:persistent</NameIDFormat>
<NameIDFormat>urn:oasis:names:tc:SAML:2.0:nameid-format:transient</NameIDFormat>
<NameIDFormat>urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified</NameIDFormat>
<NameIDFormat>urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress</NameIDFormat>
<SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"
Location="http://localhost:8080/auth/realms/master/protocol/saml" />
</IDPSSODescriptor>
</EntityDescriptor>

View file

@ -5,16 +5,6 @@
>
<IDPSSODescriptor WantAuthnRequestsSigned="true"
protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol">
<NameIDFormat>urn:oasis:names:tc:SAML:2.0:nameid-format:persistent</NameIDFormat>
<NameIDFormat>urn:oasis:names:tc:SAML:2.0:nameid-format:transient</NameIDFormat>
<NameIDFormat>urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified</NameIDFormat>
<NameIDFormat>urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress</NameIDFormat>
<SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"
Location="http://localhost:8080/auth/realms/master/protocol/saml" />
<SingleLogoutService
Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"
Location="http://localhost:8080/auth/realms/master/protocol/saml" />
<KeyDescriptor use="signing">
<dsig:KeyInfo xmlns:dsig="http://www.w3.org/2000/09/xmldsig#">
<dsig:X509Data>
@ -24,5 +14,14 @@
</dsig:X509Data>
</dsig:KeyInfo>
</KeyDescriptor>
<SingleLogoutService
Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"
Location="http://localhost:8080/auth/realms/master/protocol/saml" />
<NameIDFormat>urn:oasis:names:tc:SAML:2.0:nameid-format:persistent</NameIDFormat>
<NameIDFormat>urn:oasis:names:tc:SAML:2.0:nameid-format:transient</NameIDFormat>
<NameIDFormat>urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified</NameIDFormat>
<NameIDFormat>urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress</NameIDFormat>
<SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"
Location="http://localhost:8080/auth/realms/master/protocol/saml" />
</IDPSSODescriptor>
</EntityDescriptor>