From 977cc473bb3a52d2d3e2d76bf2d94cffcf2bfaae Mon Sep 17 00:00:00 2001 From: Hynek Mlnarik Date: Mon, 23 Jan 2023 14:43:20 +0100 Subject: [PATCH] Fix linebreaks in XML / SAML signatures See https://bugs.openjdk.org/browse/JDK-8264194 See https://issues.apache.org/jira/browse/SANTUARIO-482 Fixes: #14529 --- .../common/util/SystemPropertiesUtil.java | 5 ++++ .../testsuite/saml/BasicSamlTest.java | 23 +++++++++++++++++++ 2 files changed, 28 insertions(+) diff --git a/saml-core/src/main/java/org/keycloak/saml/common/util/SystemPropertiesUtil.java b/saml-core/src/main/java/org/keycloak/saml/common/util/SystemPropertiesUtil.java index 21d7e51526..ae2b122386 100755 --- a/saml-core/src/main/java/org/keycloak/saml/common/util/SystemPropertiesUtil.java +++ b/saml-core/src/main/java/org/keycloak/saml/common/util/SystemPropertiesUtil.java @@ -32,6 +32,11 @@ public class SystemPropertiesUtil { SecurityActions.setSystemProperty(xmlSec, "true"); } + String xmlSecOpenJdk = "com.sun.org.apache.xml.internal.security.ignoreLineBreaks"; + if (StringUtil.isNullOrEmpty(SecurityActions.getSystemProperty(xmlSecOpenJdk, ""))) { + SecurityActions.setSystemProperty(xmlSecOpenJdk, "true"); + } + // For JAXP Validation String schemaFactoryProperty = "javax.xml.validation.SchemaFactory:" + XMLConstants.W3C_XML_SCHEMA_NS_URI; if (StringUtil.isNullOrEmpty(SecurityActions.getSystemProperty(schemaFactoryProperty, ""))) { diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/saml/BasicSamlTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/saml/BasicSamlTest.java index 4b6ec269bf..6f695e5ccd 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/saml/BasicSamlTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/saml/BasicSamlTest.java @@ -2,6 +2,7 @@ package org.keycloak.testsuite.saml; import org.junit.Test; import org.keycloak.dom.saml.v2.protocol.AuthnRequestType; +import org.keycloak.protocol.saml.SamlConfigAttributes; import org.keycloak.protocol.saml.SamlProtocol; import org.keycloak.saml.SignatureAlgorithm; import org.keycloak.saml.common.constants.GeneralConstants; @@ -14,6 +15,7 @@ import org.keycloak.saml.processing.api.saml.v2.request.SAML2Request; import org.keycloak.saml.processing.core.saml.v2.common.SAMLDocumentHolder; import org.keycloak.saml.processing.web.util.RedirectBindingUtil; import org.keycloak.services.resources.RealmsResource; +import org.keycloak.testsuite.updaters.ClientAttributeUpdater; import org.keycloak.testsuite.util.KeyUtils; import org.keycloak.testsuite.util.Matchers; import org.keycloak.testsuite.util.SamlClient; @@ -48,6 +50,7 @@ import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.nullValue; import static org.hamcrest.Matchers.notNullValue; import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.matchesRegex; import static org.keycloak.saml.common.constants.JBossSAMLURIConstants.NAMEID_FORMAT_TRANSIENT; import static org.keycloak.saml.common.constants.JBossSAMLURIConstants.PROTOCOL_NSURI; import static org.keycloak.testsuite.util.ServerURLs.AUTH_SERVER_PORT; @@ -304,4 +307,24 @@ public class BasicSamlTest extends AbstractSamlTest { assertThat("AuthnRequest/NameIdPolicy Format should be Transient, but it is not", formatAttribute.getNodeValue(), is(NAMEID_FORMAT_TRANSIENT.get())); assertThat("AuthnRequest/NameIdPolicy element shouldn't contain the AllowCreate attribute when Format is set to Transient, but it does", allowCreateAttribute, nullValue()); } + + @Test + public void testSignatureContainsAllowedCharactersOnly() throws IOException { + try (var c = ClientAttributeUpdater.forClient(adminClient, REALM_NAME, SAML_CLIENT_ID_SALES_POST) + .setAttribute(SamlConfigAttributes.SAML_SERVER_SIGNATURE, "true") + .update() + ) { + SAMLDocumentHolder documentHolder = new SamlClientBuilder() + .authnRequest(getAuthServerSamlEndpoint(REALM_NAME), SAML_CLIENT_ID_SALES_POST, SAML_ASSERTION_CONSUMER_URL_SALES_POST, Binding.POST).build() + .login().user(bburkeUser).build() + .getSamlResponse(Binding.POST); + + final String signature = documentHolder.getSamlDocument() + .getElementsByTagName("dsig:SignatureValue") + .item(0).getTextContent(); + + // Corresponds to https://www.w3.org/TR/xmlschema-2/#base64Binary + assertThat(signature, matchesRegex("^[A-Za-z0-9+/ ]+[= ]*$")); + } + } }