2
0
Эх сурвалжийг харах

support configurable signature algorithm

Closes gh-8952
Arnaud Mergey 4 жил өмнө
parent
commit
9900658c92

+ 1 - 1
saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/authentication/OpenSamlAuthenticationRequestFactory.java

@@ -274,7 +274,7 @@ public class OpenSamlAuthenticationRequestFactory implements Saml2Authentication
 
 	private SignatureSigningParameters resolveSigningParameters(RelyingPartyRegistration relyingPartyRegistration) {
 		List<Credential> credentials = resolveSigningCredentials(relyingPartyRegistration);
-		List<String> algorithms = Collections.singletonList(SignatureConstants.ALGO_ID_SIGNATURE_RSA_SHA256);
+		List<String> algorithms = relyingPartyRegistration.getAssertingPartyDetails().getSigningMethodAlgorithms();
 		List<String> digests = Collections.singletonList(SignatureConstants.ALGO_ID_DIGEST_SHA256);
 		String canonicalization = SignatureConstants.ALGO_ID_C14N_EXCL_OMIT_COMMENTS;
 		SignatureSigningParametersResolver resolver = new SAMLMetadataSignatureSigningParametersResolver();

+ 37 - 2
saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/registration/RelyingPartyRegistration.java

@@ -18,6 +18,8 @@ package org.springframework.security.saml2.provider.service.registration;
 
 import java.security.PrivateKey;
 import java.security.cert.X509Certificate;
+import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.HashSet;
@@ -27,6 +29,8 @@ import java.util.Set;
 import java.util.function.Consumer;
 import java.util.function.Function;
 
+import org.opensaml.xmlsec.signature.support.SignatureConstants;
+
 import org.springframework.security.saml2.core.Saml2X509Credential;
 import org.springframework.util.Assert;
 
@@ -438,10 +442,12 @@ public final class RelyingPartyRegistration {
 
 		private final Saml2MessageBinding singleSignOnServiceBinding;
 
+		private List<String> signingMethodAlgorithms;
+
 		private AssertingPartyDetails(String entityId, boolean wantAuthnRequestsSigned,
 				Collection<Saml2X509Credential> verificationX509Credentials,
 				Collection<Saml2X509Credential> encryptionX509Credentials, String singleSignOnServiceLocation,
-				Saml2MessageBinding singleSignOnServiceBinding) {
+				Saml2MessageBinding singleSignOnServiceBinding, List<String> signingMethodAlgorithms) {
 			Assert.hasText(entityId, "entityId cannot be null or empty");
 			Assert.notNull(verificationX509Credentials, "verificationX509Credentials cannot be null");
 			for (Saml2X509Credential credential : verificationX509Credentials) {
@@ -457,12 +463,14 @@ public final class RelyingPartyRegistration {
 			}
 			Assert.notNull(singleSignOnServiceLocation, "singleSignOnServiceLocation cannot be null");
 			Assert.notNull(singleSignOnServiceBinding, "singleSignOnServiceBinding cannot be null");
+			Assert.notEmpty(signingMethodAlgorithms, "signingMethodAlgorithms cannot be empty");
 			this.entityId = entityId;
 			this.wantAuthnRequestsSigned = wantAuthnRequestsSigned;
 			this.verificationX509Credentials = verificationX509Credentials;
 			this.encryptionX509Credentials = encryptionX509Credentials;
 			this.singleSignOnServiceLocation = singleSignOnServiceLocation;
 			this.singleSignOnServiceBinding = singleSignOnServiceBinding;
+			this.signingMethodAlgorithms = signingMethodAlgorithms;
 		}
 
 		/**
@@ -542,6 +550,15 @@ public final class RelyingPartyRegistration {
 			return this.singleSignOnServiceBinding;
 		}
 
+		/**
+		 * Return the list of preferred signature algorithm URIs, in preference order.
+		 * @return the list of signature algorithm URIs
+		 * @since 5.5
+		 */
+		public List<String> getSigningMethodAlgorithms() {
+			return this.signingMethodAlgorithms;
+		}
+
 		public static final class Builder {
 
 			private String entityId;
@@ -556,6 +573,8 @@ public final class RelyingPartyRegistration {
 
 			private Saml2MessageBinding singleSignOnServiceBinding = Saml2MessageBinding.REDIRECT;
 
+			private List<String> signingMethodAlgorithms = new ArrayList<>();
+
 			/**
 			 * Set the asserting party's <a href=
 			 * "https://wiki.shibboleth.net/confluence/display/CONCEPT/EntityNaming">EntityID</a>.
@@ -639,15 +658,31 @@ public final class RelyingPartyRegistration {
 				return this;
 			}
 
+			/**
+			 * Apply this {@link Consumer} to the list of signature algorithm URIs
+			 * @param signingMethodAlgorithmsConsumer a {@link Consumer} of the list of
+			 * signature algorithm URIs
+			 * @return this {@code Builder}
+			 * @since 5.5
+			 */
+			public Builder signingMethodAlgorithms(Consumer<List<String>> signingMethodAlgorithmsConsumer) {
+				signingMethodAlgorithmsConsumer.accept(this.signingMethodAlgorithms);
+				return this;
+			}
+
 			/**
 			 * Creates an immutable ProviderDetails object representing the configuration
 			 * for an Identity Provider, IDP
 			 * @return immutable ProviderDetails object
 			 */
 			public AssertingPartyDetails build() {
+				List<String> signingMethodAlgorithmsCopy = this.signingMethodAlgorithms.isEmpty()
+						? Arrays.asList(SignatureConstants.ALGO_ID_SIGNATURE_RSA_SHA256)
+						: Collections.unmodifiableList(this.signingMethodAlgorithms);
+
 				return new AssertingPartyDetails(this.entityId, this.wantAuthnRequestsSigned,
 						this.verificationX509Credentials, this.encryptionX509Credentials,
-						this.singleSignOnServiceLocation, this.singleSignOnServiceBinding);
+						this.singleSignOnServiceLocation, this.singleSignOnServiceBinding, signingMethodAlgorithmsCopy);
 			}
 
 		}

+ 12 - 0
saml2/saml2-service-provider/src/test/java/org/springframework/security/saml2/provider/service/authentication/OpenSamlAuthenticationProviderTests.java

@@ -52,6 +52,7 @@ import org.opensaml.saml.saml2.core.impl.EncryptedAssertionBuilder;
 import org.opensaml.saml.saml2.core.impl.EncryptedIDBuilder;
 import org.opensaml.saml.saml2.core.impl.NameIDBuilder;
 import org.opensaml.xmlsec.encryption.impl.EncryptedDataBuilder;
+import org.opensaml.xmlsec.signature.support.SignatureConstants;
 import org.w3c.dom.Element;
 
 import org.springframework.core.convert.converter.Converter;
@@ -463,6 +464,17 @@ public class OpenSamlAuthenticationProviderTests {
 		verify(context, atLeastOnce()).getStaticParameters();
 	}
 
+	@Test
+	public void authenticateWithSHA1SignatureThenItSucceeds() throws Exception {
+		Response response = TestOpenSamlObjects.response();
+		Assertion assertion = TestOpenSamlObjects.signed(TestOpenSamlObjects.assertion(),
+				TestSaml2X509Credentials.assertingPartySigningCredential(), RELYING_PARTY_ENTITY_ID,
+				SignatureConstants.ALGO_ID_SIGNATURE_RSA_SHA1);
+		response.getAssertions().add(assertion);
+		Saml2AuthenticationToken token = token(response, verifying(registration()));
+		this.provider.authenticate(token);
+	}
+
 	@Test
 	public void setAssertionValidatorWhenNullThenIllegalArgument() {
 		// @formatter:off

+ 16 - 0
saml2/saml2-service-provider/src/test/java/org/springframework/security/saml2/provider/service/authentication/OpenSamlAuthenticationRequestFactoryTests.java

@@ -240,6 +240,22 @@ public class OpenSamlAuthenticationRequestFactoryTests {
 		assertThat(inflated).contains("ProtocolBinding=\"" + SAMLConstants.SAML2_REDIRECT_BINDING_URI + "\"");
 	}
 
+	@Test
+	public void createRedirectAuthenticationRequestWhenSHA1SignRequestThenSignatureIsPresent() {
+		RelyingPartyRegistration relyingPartyRegistration = this.relyingPartyRegistrationBuilder
+				.assertingPartyDetails(
+						(a) -> a.signingMethodAlgorithms((c) -> c.add(SignatureConstants.ALGO_ID_SIGNATURE_RSA_SHA1)))
+				.build();
+		Saml2AuthenticationRequestContext context = this.contextBuilder.relayState("Relay State Value")
+				.relyingPartyRegistration(relyingPartyRegistration).build();
+		Saml2RedirectAuthenticationRequest result = this.factory.createRedirectAuthenticationRequest(context);
+		assertThat(result.getSamlRequest()).isNotEmpty();
+		assertThat(result.getRelayState()).isEqualTo("Relay State Value");
+		assertThat(result.getSigAlg()).isEqualTo(SignatureConstants.ALGO_ID_SIGNATURE_RSA_SHA1);
+		assertThat(result.getSignature()).isNotNull();
+		assertThat(result.getBinding()).isEqualTo(Saml2MessageBinding.REDIRECT);
+	}
+
 	private AuthnRequest getAuthNRequest(Saml2MessageBinding binding) {
 		AbstractSaml2AuthenticationRequest result = (binding == Saml2MessageBinding.REDIRECT)
 				? this.factory.createRedirectAuthenticationRequest(this.context)

+ 7 - 2
saml2/saml2-service-provider/src/test/java/org/springframework/security/saml2/provider/service/authentication/TestOpenSamlObjects.java

@@ -205,11 +205,12 @@ public final class TestOpenSamlObjects {
 		return CredentialSupport.getSimpleCredential(credential.getCertificate(), credential.getPrivateKey());
 	}
 
-	static <T extends SignableSAMLObject> T signed(T signable, Saml2X509Credential credential, String entityId) {
+	static <T extends SignableSAMLObject> T signed(T signable, Saml2X509Credential credential, String entityId,
+			String signAlgorithmUri) {
 		SignatureSigningParameters parameters = new SignatureSigningParameters();
 		Credential signingCredential = getSigningCredential(credential, entityId);
 		parameters.setSigningCredential(signingCredential);
-		parameters.setSignatureAlgorithm(SignatureConstants.ALGO_ID_SIGNATURE_RSA_SHA256);
+		parameters.setSignatureAlgorithm(signAlgorithmUri);
 		parameters.setSignatureReferenceDigestMethod(SignatureConstants.ALGO_ID_DIGEST_SHA256);
 		parameters.setSignatureCanonicalizationAlgorithm(SignatureConstants.ALGO_ID_C14N_EXCL_OMIT_COMMENTS);
 		try {
@@ -221,6 +222,10 @@ public final class TestOpenSamlObjects {
 		return signable;
 	}
 
+	static <T extends SignableSAMLObject> T signed(T signable, Saml2X509Credential credential, String entityId) {
+		return signed(signable, credential, entityId, SignatureConstants.ALGO_ID_SIGNATURE_RSA_SHA256);
+	}
+
 	static EncryptedAssertion encrypted(Assertion assertion, Saml2X509Credential credential) {
 		X509Certificate certificate = credential.getCertificate();
 		Encrypter encrypter = getEncrypter(certificate);