diff --git a/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/web/authentication/logout/OpenSamlSigningUtils.java b/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/web/authentication/logout/OpenSamlSigningUtils.java index 45b0023c2f6..f30c00cf0e1 100644 --- a/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/web/authentication/logout/OpenSamlSigningUtils.java +++ b/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/web/authentication/logout/OpenSamlSigningUtils.java @@ -42,6 +42,9 @@ import org.opensaml.xmlsec.criterion.SignatureSigningConfigurationCriterion; import org.opensaml.xmlsec.crypto.XMLSigningUtil; import org.opensaml.xmlsec.impl.BasicSignatureSigningConfiguration; +import org.opensaml.xmlsec.keyinfo.KeyInfoGeneratorManager; +import org.opensaml.xmlsec.keyinfo.NamedKeyInfoGeneratorManager; +import org.opensaml.xmlsec.keyinfo.impl.X509KeyInfoGeneratorFactory; import org.opensaml.xmlsec.signature.SignableXMLObject; import org.opensaml.xmlsec.signature.support.SignatureConstants; import org.opensaml.xmlsec.signature.support.SignatureSupport; @@ -103,6 +106,7 @@ private static SignatureSigningParameters resolveSigningParameters( signingConfiguration.setSignatureAlgorithms(algorithms); signingConfiguration.setSignatureReferenceDigestMethods(digests); signingConfiguration.setSignatureCanonicalizationAlgorithm(canonicalization); + signingConfiguration.setKeyInfoGeneratorManager(buildSignatureKeyInfoGeneratorManager()); criteria.add(new SignatureSigningConfigurationCriterion(signingConfiguration)); try { SignatureSigningParameters parameters = resolver.resolveSingle(criteria); @@ -114,6 +118,22 @@ private static SignatureSigningParameters resolveSigningParameters( } } + private static NamedKeyInfoGeneratorManager buildSignatureKeyInfoGeneratorManager() { + final NamedKeyInfoGeneratorManager namedManager = new NamedKeyInfoGeneratorManager(); + + namedManager.setUseDefaultManager(true); + final KeyInfoGeneratorManager defaultManager = namedManager.getDefaultManager(); + + // Generator for X509Credentials + final X509KeyInfoGeneratorFactory x509Factory = new X509KeyInfoGeneratorFactory(); + x509Factory.setEmitEntityCertificate(true); + x509Factory.setEmitEntityCertificateChain(true); + + defaultManager.registerFactory(x509Factory); + + return namedManager; + } + private static List resolveSigningCredentials(RelyingPartyRegistration relyingPartyRegistration) { List credentials = new ArrayList<>(); for (Saml2X509Credential x509Credential : relyingPartyRegistration.getSigningX509Credentials()) { diff --git a/saml2/saml2-service-provider/src/test/java/org/springframework/security/saml2/provider/service/authentication/TestOpenSamlObjects.java b/saml2/saml2-service-provider/src/test/java/org/springframework/security/saml2/provider/service/authentication/TestOpenSamlObjects.java index 7acb769f4ab..0f38823e224 100644 --- a/saml2/saml2-service-provider/src/test/java/org/springframework/security/saml2/provider/service/authentication/TestOpenSamlObjects.java +++ b/saml2/saml2-service-provider/src/test/java/org/springframework/security/saml2/provider/service/authentication/TestOpenSamlObjects.java @@ -384,6 +384,22 @@ public static LogoutResponse assertingPartyLogoutResponse(RelyingPartyRegistrati return logoutResponse; } + public static LogoutRequest relyingPartyLogoutRequest(RelyingPartyRegistration registration) { + LogoutRequestBuilder logoutRequestBuilder = new LogoutRequestBuilder(); + LogoutRequest logoutRequest = logoutRequestBuilder.buildObject(); + logoutRequest.setID("id"); + NameIDBuilder nameIdBuilder = new NameIDBuilder(); + NameID nameId = nameIdBuilder.buildObject(); + nameId.setValue("user"); + logoutRequest.setNameID(nameId); + IssuerBuilder issuerBuilder = new IssuerBuilder(); + Issuer issuer = issuerBuilder.buildObject(); + issuer.setValue(registration.getAssertingPartyDetails().getEntityId()); + logoutRequest.setIssuer(issuer); + logoutRequest.setDestination(registration.getAssertingPartyDetails().getSingleLogoutServiceLocation()); + return logoutRequest; + } + static T build(QName qName) { return (T) XMLObjectProviderRegistrySupport.getBuilderFactory().getBuilder(qName).buildObject(qName); } diff --git a/saml2/saml2-service-provider/src/test/java/org/springframework/security/saml2/provider/service/authentication/logout/OpenSamlSigningUtils.java b/saml2/saml2-service-provider/src/test/java/org/springframework/security/saml2/provider/service/authentication/logout/OpenSamlSigningUtils.java index bc302da1e28..9855a39267b 100644 --- a/saml2/saml2-service-provider/src/test/java/org/springframework/security/saml2/provider/service/authentication/logout/OpenSamlSigningUtils.java +++ b/saml2/saml2-service-provider/src/test/java/org/springframework/security/saml2/provider/service/authentication/logout/OpenSamlSigningUtils.java @@ -42,6 +42,9 @@ import org.opensaml.xmlsec.criterion.SignatureSigningConfigurationCriterion; import org.opensaml.xmlsec.crypto.XMLSigningUtil; import org.opensaml.xmlsec.impl.BasicSignatureSigningConfiguration; +import org.opensaml.xmlsec.keyinfo.KeyInfoGeneratorManager; +import org.opensaml.xmlsec.keyinfo.NamedKeyInfoGeneratorManager; +import org.opensaml.xmlsec.keyinfo.impl.X509KeyInfoGeneratorFactory; import org.opensaml.xmlsec.signature.SignableXMLObject; import org.opensaml.xmlsec.signature.support.SignatureConstants; import org.opensaml.xmlsec.signature.support.SignatureSupport; @@ -103,6 +106,7 @@ private static SignatureSigningParameters resolveSigningParameters( signingConfiguration.setSignatureAlgorithms(algorithms); signingConfiguration.setSignatureReferenceDigestMethods(digests); signingConfiguration.setSignatureCanonicalizationAlgorithm(canonicalization); + signingConfiguration.setKeyInfoGeneratorManager(buildSignatureKeyInfoGeneratorManager()); criteria.add(new SignatureSigningConfigurationCriterion(signingConfiguration)); try { SignatureSigningParameters parameters = resolver.resolveSingle(criteria); @@ -114,6 +118,22 @@ private static SignatureSigningParameters resolveSigningParameters( } } + private static NamedKeyInfoGeneratorManager buildSignatureKeyInfoGeneratorManager() { + final NamedKeyInfoGeneratorManager namedManager = new NamedKeyInfoGeneratorManager(); + + namedManager.setUseDefaultManager(true); + final KeyInfoGeneratorManager defaultManager = namedManager.getDefaultManager(); + + // Generator for X509Credentials + final X509KeyInfoGeneratorFactory x509Factory = new X509KeyInfoGeneratorFactory(); + x509Factory.setEmitEntityCertificate(true); + x509Factory.setEmitEntityCertificateChain(true); + + defaultManager.registerFactory(x509Factory); + + return namedManager; + } + private static List resolveSigningCredentials(RelyingPartyRegistration relyingPartyRegistration) { List credentials = new ArrayList<>(); for (Saml2X509Credential x509Credential : relyingPartyRegistration.getSigningX509Credentials()) { diff --git a/saml2/saml2-service-provider/src/test/java/org/springframework/security/saml2/provider/service/web/authentication/logout/Saml2LogoutSigningUtilsTests.java b/saml2/saml2-service-provider/src/test/java/org/springframework/security/saml2/provider/service/web/authentication/logout/Saml2LogoutSigningUtilsTests.java new file mode 100644 index 00000000000..b1e63672d62 --- /dev/null +++ b/saml2/saml2-service-provider/src/test/java/org/springframework/security/saml2/provider/service/web/authentication/logout/Saml2LogoutSigningUtilsTests.java @@ -0,0 +1,57 @@ +/* + * Copyright 2002-2021 the original author or authors. + * + * 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 + * + * https://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.springframework.security.saml2.provider.service.web.authentication.logout; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.opensaml.saml.saml2.core.LogoutRequest; +import org.opensaml.xmlsec.signature.Signature; + +import org.springframework.security.saml2.core.TestSaml2X509Credentials; +import org.springframework.security.saml2.provider.service.authentication.TestOpenSamlObjects; +import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Test open SAML Logout signatures + */ +public class Saml2LogoutSigningUtilsTests { + + private RelyingPartyRegistration registration; + + @BeforeEach + public void setup() { + this.registration = RelyingPartyRegistration.withRegistrationId("saml-idp") + .entityId("https://some.idp.example.com/entity-id").signingX509Credentials((c) -> { + c.add(TestSaml2X509Credentials.relyingPartySigningCredential()); + c.add(TestSaml2X509Credentials.assertingPartySigningCredential()); + }).assertingPartyDetails((c) -> c.entityId("https://some.idp.example.com/entity-id") + .singleSignOnServiceLocation("https://some.idp.example.com/service-location")) + .build(); + } + + @Test + public void whenSigningLogoutRequestRPThenKeyInfoIsPartOfTheSignature() { + LogoutRequest logoutRequest = TestOpenSamlObjects.relyingPartyLogoutRequest(this.registration); + OpenSamlSigningUtils.sign(logoutRequest, this.registration); + Signature signature = logoutRequest.getSignature(); + assertThat(signature).isNotNull(); + assertThat(signature.getKeyInfo()).isNotNull(); + } + +}