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 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 ### 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: 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,7 +23,9 @@ package org.keycloak.saml;
*/ */
public class SPMetadataDescriptor { 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 = String descriptor =
"<EntityDescriptor xmlns=\"urn:oasis:names:tc:SAML:2.0:metadata\" entityID=\"" + entityId + "\">\n" + "<EntityDescriptor xmlns=\"urn:oasis:names:tc:SAML:2.0:metadata\" entityID=\"" + entityId + "\">\n" +
" <SPSSODescriptor AuthnRequestsSigned=\"" + wantAuthnRequestsSigned + "\" WantAssertionsSigned=\"" + wantAssertionsSigned + "\"\n" + " <SPSSODescriptor AuthnRequestsSigned=\"" + wantAuthnRequestsSigned + "\" WantAssertionsSigned=\"" + wantAssertionsSigned + "\"\n" +
@ -31,6 +33,9 @@ public class SPMetadataDescriptor {
if (wantAuthnRequestsSigned && signingCerts != null) { if (wantAuthnRequestsSigned && signingCerts != null) {
descriptor += signingCerts; descriptor += signingCerts;
} }
if (wantAssertionsEncrypted && encryptionCerts != null) {
descriptor += encryptionCerts;
}
descriptor += descriptor +=
" <SingleLogoutService Binding=\"" + binding + "\" Location=\"" + logoutEndpoint + "\"/>\n" + " <SingleLogoutService Binding=\"" + binding + "\" Location=\"" + logoutEndpoint + "\"/>\n" +
" <NameIDFormat>" + nameIDPolicyFormat + "\n" + " <NameIDFormat>" + nameIDPolicyFormat + "\n" +

View file

@ -54,6 +54,7 @@ import java.util.Set;
import java.util.TreeSet; import java.util.TreeSet;
import org.keycloak.dom.saml.v2.metadata.KeyTypes; import org.keycloak.dom.saml.v2.metadata.KeyTypes;
import org.keycloak.keys.KeyMetadata; import org.keycloak.keys.KeyMetadata;
import org.keycloak.keys.KeyMetadata.Status;
import org.keycloak.saml.processing.core.util.KeycloakKeySamlExtensionGenerator; import org.keycloak.saml.processing.core.util.KeycloakKeySamlExtensionGenerator;
import org.keycloak.sessions.AuthenticationSessionModel; import org.keycloak.sessions.AuthenticationSessionModel;
@ -237,18 +238,27 @@ public class SAMLIdentityProvider extends AbstractIdentityProvider<SAMLIdentityP
boolean wantAuthnRequestsSigned = getConfig().isWantAuthnRequestsSigned(); boolean wantAuthnRequestsSigned = getConfig().isWantAuthnRequestsSigned();
boolean wantAssertionsSigned = getConfig().isWantAssertionsSigned(); boolean wantAssertionsSigned = getConfig().isWantAssertionsSigned();
boolean wantAssertionsEncrypted = getConfig().isWantAssertionsEncrypted();
String entityId = getEntityId(uriInfo, realm); String entityId = getEntityId(uriInfo, realm);
String nameIDPolicyFormat = getConfig().getNameIDPolicyFormat(); 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 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()) ? (int) (o2.getProviderPriority() - o1.getProviderPriority())
: (o1.getStatus() == KeyMetadata.Status.PASSIVE ? 1 : -1)); : (o1.getStatus() == KeyMetadata.Status.PASSIVE ? 1 : -1));
keys.addAll(session.keys().getRsaKeys(realm, false)); keys.addAll(session.keys().getRsaKeys(realm, false));
for (RsaKeyMetadata key : keys) { 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(); return Response.ok(descriptor, MediaType.APPLICATION_XML_TYPE).build();
} }

View file

@ -127,7 +127,7 @@ public class SamlIDPDescriptorClientInstallation implements ClientInstallationPr
@Override @Override
public String getHelpText() { 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 @Override

View file

@ -47,8 +47,10 @@ public class SamlSPDescriptorClientInstallation implements ClientInstallationPro
String nameIdFormat = samlClient.getNameIDFormat(); String nameIdFormat = samlClient.getNameIDFormat();
if (nameIdFormat == null) nameIdFormat = SamlProtocol.SAML_DEFAULT_NAMEID_FORMAT; if (nameIdFormat == null) nameIdFormat = SamlProtocol.SAML_DEFAULT_NAMEID_FORMAT;
String spCertificate = SPMetadataDescriptor.xmlKeyInfo(" ", null, samlClient.getClientSigningCertificate(), KeyTypes.SIGNING.value(), true); 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, 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 @Override

View file

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

View file

@ -78,7 +78,7 @@ public class ValidationTest {
public void testBrokerExportDescriptor() throws Exception { public void testBrokerExportDescriptor() throws Exception {
URL schemaFile = getClass().getResource("/schema/saml/v2/saml-schema-metadata-2.0.xsd"); URL schemaFile = getClass().getResource("/schema/saml/v2/saml-schema-metadata-2.0.xsd");
Source xmlFile = new StreamSource(new ByteArrayInputStream(SPMetadataDescriptor.getSPDescriptor( 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"); ).getBytes()), "SP Descriptor");
SchemaFactory schemaFactory = SchemaFactory SchemaFactory schemaFactory = SchemaFactory
.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI); .newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);