Merge pull request #4351 from hmlnarik/KEYCLOAK-4446-Failed-to-process-response-when-reject-consent-with-turned-on-encryption

KEYCLOAK-4775 Added encryption certificate to SAML metadata
This commit is contained in:
Marek Posolda 2017-07-27 14:26:41 +02:00 committed by GitHub
commit 121ec2603b
7 changed files with 55 additions and 10 deletions

View file

@ -29,6 +29,15 @@ When starting the server it can also import a realm from a json file:
mvn exec:java -Pkeycloak-server -Dimport=testrealm.json
When starting the server, https transport can be set up by setting keystore containing the server certificate
and https port, optionally setting the truststore.
mvn exec:java -Pkeycloak-server \
-Djavax.net.ssl.trustStore=/path/to/truststore.jks \
-Djavax.net.ssl.keyStore=/path/to/keystore.jks \
-Djavax.net.ssl.keyStorePassword=CHANGEME \
-Dkeycloak.port.https=8443
### Live edit of html and styles
The Keycloak test server can load resources directly from the filesystem instead of the classpath. This allows editing html, styles and updating images without restarting the server. To make the server use resources from the filesystem start with:

View file

@ -23,14 +23,19 @@ package org.keycloak.saml;
*/
public class SPMetadataDescriptor {
public static String getSPDescriptor(String binding, String assertionEndpoint, String logoutEndpoint, boolean wantAuthnRequestsSigned, boolean wantAssertionsSigned, String entityId, String nameIDPolicyFormat, String signingCerts) {
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";
if (wantAuthnRequestsSigned && signingCerts != null) {
if (wantAuthnRequestsSigned && signingCerts != null) {
descriptor += signingCerts;
}
if (wantAssertionsEncrypted && encryptionCerts != null) {
descriptor += encryptionCerts;
}
descriptor +=
" <SingleLogoutService Binding=\"" + binding + "\" Location=\"" + logoutEndpoint + "\"/>\n" +
" <NameIDFormat>" + nameIDPolicyFormat + "\n" +

View file

@ -54,6 +54,7 @@ import java.util.Set;
import java.util.TreeSet;
import org.keycloak.dom.saml.v2.metadata.KeyTypes;
import org.keycloak.keys.KeyMetadata;
import org.keycloak.keys.KeyMetadata.Status;
import org.keycloak.saml.processing.core.util.KeycloakKeySamlExtensionGenerator;
import org.keycloak.sessions.AuthenticationSessionModel;
@ -237,18 +238,27 @@ public class SAMLIdentityProvider extends AbstractIdentityProvider<SAMLIdentityP
boolean wantAuthnRequestsSigned = getConfig().isWantAuthnRequestsSigned();
boolean wantAssertionsSigned = getConfig().isWantAssertionsSigned();
boolean wantAssertionsEncrypted = getConfig().isWantAssertionsEncrypted();
String entityId = getEntityId(uriInfo, realm);
String nameIDPolicyFormat = getConfig().getNameIDPolicyFormat();
StringBuilder keysString = new StringBuilder();
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() == KeyMetadata.Status.PASSIVE ? 1 : -1));
keys.addAll(session.keys().getRsaKeys(realm, false));
for (RsaKeyMetadata key : keys) {
addKeyInfo(keysString, key, KeyTypes.SIGNING.value());
addKeyInfo(signingKeysString, key, KeyTypes.SIGNING.value());
if (key.getStatus() == Status.ACTIVE) {
addKeyInfo(encryptionKeysString, key, KeyTypes.ENCRYPTION.value());
}
}
String descriptor = SPMetadataDescriptor.getSPDescriptor(authnBinding, endpoint, endpoint, wantAuthnRequestsSigned, wantAssertionsSigned, entityId, nameIDPolicyFormat, keysString.toString());
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();
}

View file

@ -127,7 +127,7 @@ public class SamlIDPDescriptorClientInstallation implements ClientInstallationPr
@Override
public String getHelpText() {
return "SAML Metadata IDSSODescriptor tailored for the client. This is special because not every client may require things like digital signatures";
return "SAML Metadata IDPSSODescriptor tailored for the client. This is special because not every client may require things like digital signatures";
}
@Override

View file

@ -47,8 +47,10 @@ public class SamlSPDescriptorClientInstallation implements ClientInstallationPro
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(JBossSAMLURIConstants.SAML_HTTP_POST_BINDING.get(), assertionUrl, logoutUrl,
samlClient.requiresClientSignature(), samlClient.requiresAssertionSignature(), client.getClientId(), nameIdFormat, spCertificate);
samlClient.requiresClientSignature(), samlClient.requiresAssertionSignature(), samlClient.requiresEncryption(),
client.getClientId(), nameIdFormat, spCertificate, encCertificate);
}
@Override

View file

@ -39,7 +39,6 @@ import org.keycloak.services.managers.RealmManager;
import org.keycloak.services.resources.KeycloakApplication;
import org.keycloak.testsuite.util.cli.TestsuiteCLI;
import org.keycloak.util.JsonSerialization;
import org.mvel2.util.Make;
import javax.servlet.DispatcherType;
import java.io.File;
@ -51,6 +50,7 @@ import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import javax.net.ssl.SSLContext;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
@ -64,6 +64,7 @@ public class KeycloakServer {
public static class KeycloakServerConfig {
private String host = "localhost";
private int port = 8081;
private int portHttps = -1;
private int workerThreads = Math.max(Runtime.getRuntime().availableProcessors(), 2) * 8;
private String resourcesHome;
@ -75,6 +76,10 @@ public class KeycloakServer {
return port;
}
public int getPortHttps() {
return portHttps;
}
public String getResourcesHome() {
return resourcesHome;
}
@ -87,6 +92,10 @@ public class KeycloakServer {
this.port = port;
}
public void setPortHttps(int portHttps) {
this.portHttps = portHttps;
}
public void setResourcesHome(String resourcesHome) {
this.resourcesHome = resourcesHome;
}
@ -140,6 +149,10 @@ public class KeycloakServer {
config.setPort(Integer.valueOf(System.getProperty("keycloak.port")));
}
if (System.getProperty("keycloak.port.https") != null) {
config.setPortHttps(Integer.valueOf(System.getProperty("keycloak.port.https")));
}
if (System.getProperty("keycloak.bind.address") != null) {
config.setHost(System.getProperty("keycloak.bind.address"));
}
@ -312,6 +325,10 @@ public class KeycloakServer {
.setWorkerThreads(config.getWorkerThreads())
.setIoThreads(config.getWorkerThreads() / 8);
if (config.getPortHttps() != -1) {
builder = builder.addHttpsListener(config.getPortHttps(), config.getHost(), SSLContext.getDefault());
}
server = new UndertowJaxrsServer();
try {
server.start(builder);
@ -350,7 +367,9 @@ public class KeycloakServer {
info("Loading resources from " + config.getResourcesHome());
}
info("Started Keycloak (http://" + config.getHost() + ":" + config.getPort() + "/auth) in "
info("Started Keycloak (http://" + config.getHost() + ":" + config.getPort() + "/auth"
+ (config.getPortHttps() > 0 ? ", https://" + config.getHost() + ":" + config.getPortHttps()+ "/auth" : "")
+ ") in "
+ (System.currentTimeMillis() - start) + " ms\n");
} catch (RuntimeException e) {
server.stop();

View file

@ -78,7 +78,7 @@ public class ValidationTest {
public void testBrokerExportDescriptor() throws Exception {
URL schemaFile = getClass().getResource("/schema/saml/v2/saml-schema-metadata-2.0.xsd");
Source xmlFile = new StreamSource(new ByteArrayInputStream(SPMetadataDescriptor.getSPDescriptor(
"POST", "http://realm/assertion", "http://realm/logout", true, false, "test", SamlProtocol.SAML_DEFAULT_NAMEID_FORMAT, KeycloakModelUtils.generateKeyPairCertificate("test").getCertificate()
"POST", "http://realm/assertion", "http://realm/logout", true, false, false, "test", SamlProtocol.SAML_DEFAULT_NAMEID_FORMAT, KeycloakModelUtils.generateKeyPairCertificate("test").getCertificate(), ""
).getBytes()), "SP Descriptor");
SchemaFactory schemaFactory = SchemaFactory
.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);