Skip to content

Commit 9900658

Browse files
amergeyjzheaux
authored andcommitted
support configurable signature algorithm
Closes gh-8952
1 parent 5c8972b commit 9900658

File tree

5 files changed

+73
-5
lines changed

5 files changed

+73
-5
lines changed

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -274,7 +274,7 @@ private String serialize(AuthnRequest authnRequest) {
274274

275275
private SignatureSigningParameters resolveSigningParameters(RelyingPartyRegistration relyingPartyRegistration) {
276276
List<Credential> credentials = resolveSigningCredentials(relyingPartyRegistration);
277-
List<String> algorithms = Collections.singletonList(SignatureConstants.ALGO_ID_SIGNATURE_RSA_SHA256);
277+
List<String> algorithms = relyingPartyRegistration.getAssertingPartyDetails().getSigningMethodAlgorithms();
278278
List<String> digests = Collections.singletonList(SignatureConstants.ALGO_ID_DIGEST_SHA256);
279279
String canonicalization = SignatureConstants.ALGO_ID_C14N_EXCL_OMIT_COMMENTS;
280280
SignatureSigningParametersResolver resolver = new SAMLMetadataSignatureSigningParametersResolver();

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

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818

1919
import java.security.PrivateKey;
2020
import java.security.cert.X509Certificate;
21+
import java.util.ArrayList;
22+
import java.util.Arrays;
2123
import java.util.Collection;
2224
import java.util.Collections;
2325
import java.util.HashSet;
@@ -27,6 +29,8 @@
2729
import java.util.function.Consumer;
2830
import java.util.function.Function;
2931

32+
import org.opensaml.xmlsec.signature.support.SignatureConstants;
33+
3034
import org.springframework.security.saml2.core.Saml2X509Credential;
3135
import org.springframework.util.Assert;
3236

@@ -438,10 +442,12 @@ public static final class AssertingPartyDetails {
438442

439443
private final Saml2MessageBinding singleSignOnServiceBinding;
440444

445+
private List<String> signingMethodAlgorithms;
446+
441447
private AssertingPartyDetails(String entityId, boolean wantAuthnRequestsSigned,
442448
Collection<Saml2X509Credential> verificationX509Credentials,
443449
Collection<Saml2X509Credential> encryptionX509Credentials, String singleSignOnServiceLocation,
444-
Saml2MessageBinding singleSignOnServiceBinding) {
450+
Saml2MessageBinding singleSignOnServiceBinding, List<String> signingMethodAlgorithms) {
445451
Assert.hasText(entityId, "entityId cannot be null or empty");
446452
Assert.notNull(verificationX509Credentials, "verificationX509Credentials cannot be null");
447453
for (Saml2X509Credential credential : verificationX509Credentials) {
@@ -457,12 +463,14 @@ private AssertingPartyDetails(String entityId, boolean wantAuthnRequestsSigned,
457463
}
458464
Assert.notNull(singleSignOnServiceLocation, "singleSignOnServiceLocation cannot be null");
459465
Assert.notNull(singleSignOnServiceBinding, "singleSignOnServiceBinding cannot be null");
466+
Assert.notEmpty(signingMethodAlgorithms, "signingMethodAlgorithms cannot be empty");
460467
this.entityId = entityId;
461468
this.wantAuthnRequestsSigned = wantAuthnRequestsSigned;
462469
this.verificationX509Credentials = verificationX509Credentials;
463470
this.encryptionX509Credentials = encryptionX509Credentials;
464471
this.singleSignOnServiceLocation = singleSignOnServiceLocation;
465472
this.singleSignOnServiceBinding = singleSignOnServiceBinding;
473+
this.signingMethodAlgorithms = signingMethodAlgorithms;
466474
}
467475

468476
/**
@@ -542,6 +550,15 @@ public Saml2MessageBinding getSingleSignOnServiceBinding() {
542550
return this.singleSignOnServiceBinding;
543551
}
544552

553+
/**
554+
* Return the list of preferred signature algorithm URIs, in preference order.
555+
* @return the list of signature algorithm URIs
556+
* @since 5.5
557+
*/
558+
public List<String> getSigningMethodAlgorithms() {
559+
return this.signingMethodAlgorithms;
560+
}
561+
545562
public static final class Builder {
546563

547564
private String entityId;
@@ -556,6 +573,8 @@ public static final class Builder {
556573

557574
private Saml2MessageBinding singleSignOnServiceBinding = Saml2MessageBinding.REDIRECT;
558575

576+
private List<String> signingMethodAlgorithms = new ArrayList<>();
577+
559578
/**
560579
* Set the asserting party's <a href=
561580
* "https://wiki.shibboleth.net/confluence/display/CONCEPT/EntityNaming">EntityID</a>.
@@ -639,15 +658,31 @@ public Builder singleSignOnServiceBinding(Saml2MessageBinding singleSignOnServic
639658
return this;
640659
}
641660

661+
/**
662+
* Apply this {@link Consumer} to the list of signature algorithm URIs
663+
* @param signingMethodAlgorithmsConsumer a {@link Consumer} of the list of
664+
* signature algorithm URIs
665+
* @return this {@code Builder}
666+
* @since 5.5
667+
*/
668+
public Builder signingMethodAlgorithms(Consumer<List<String>> signingMethodAlgorithmsConsumer) {
669+
signingMethodAlgorithmsConsumer.accept(this.signingMethodAlgorithms);
670+
return this;
671+
}
672+
642673
/**
643674
* Creates an immutable ProviderDetails object representing the configuration
644675
* for an Identity Provider, IDP
645676
* @return immutable ProviderDetails object
646677
*/
647678
public AssertingPartyDetails build() {
679+
List<String> signingMethodAlgorithmsCopy = this.signingMethodAlgorithms.isEmpty()
680+
? Arrays.asList(SignatureConstants.ALGO_ID_SIGNATURE_RSA_SHA256)
681+
: Collections.unmodifiableList(this.signingMethodAlgorithms);
682+
648683
return new AssertingPartyDetails(this.entityId, this.wantAuthnRequestsSigned,
649684
this.verificationX509Credentials, this.encryptionX509Credentials,
650-
this.singleSignOnServiceLocation, this.singleSignOnServiceBinding);
685+
this.singleSignOnServiceLocation, this.singleSignOnServiceBinding, signingMethodAlgorithmsCopy);
651686
}
652687

653688
}

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

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@
5252
import org.opensaml.saml.saml2.core.impl.EncryptedIDBuilder;
5353
import org.opensaml.saml.saml2.core.impl.NameIDBuilder;
5454
import org.opensaml.xmlsec.encryption.impl.EncryptedDataBuilder;
55+
import org.opensaml.xmlsec.signature.support.SignatureConstants;
5556
import org.w3c.dom.Element;
5657

5758
import org.springframework.core.convert.converter.Converter;
@@ -463,6 +464,17 @@ public void authenticateWhenValidationContextCustomizedThenUsers() {
463464
verify(context, atLeastOnce()).getStaticParameters();
464465
}
465466

467+
@Test
468+
public void authenticateWithSHA1SignatureThenItSucceeds() throws Exception {
469+
Response response = TestOpenSamlObjects.response();
470+
Assertion assertion = TestOpenSamlObjects.signed(TestOpenSamlObjects.assertion(),
471+
TestSaml2X509Credentials.assertingPartySigningCredential(), RELYING_PARTY_ENTITY_ID,
472+
SignatureConstants.ALGO_ID_SIGNATURE_RSA_SHA1);
473+
response.getAssertions().add(assertion);
474+
Saml2AuthenticationToken token = token(response, verifying(registration()));
475+
this.provider.authenticate(token);
476+
}
477+
466478
@Test
467479
public void setAssertionValidatorWhenNullThenIllegalArgument() {
468480
// @formatter:off

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

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -240,6 +240,22 @@ public void createPostAuthenticationRequestWhenAssertionConsumerServiceBindingTh
240240
assertThat(inflated).contains("ProtocolBinding=\"" + SAMLConstants.SAML2_REDIRECT_BINDING_URI + "\"");
241241
}
242242

243+
@Test
244+
public void createRedirectAuthenticationRequestWhenSHA1SignRequestThenSignatureIsPresent() {
245+
RelyingPartyRegistration relyingPartyRegistration = this.relyingPartyRegistrationBuilder
246+
.assertingPartyDetails(
247+
(a) -> a.signingMethodAlgorithms((c) -> c.add(SignatureConstants.ALGO_ID_SIGNATURE_RSA_SHA1)))
248+
.build();
249+
Saml2AuthenticationRequestContext context = this.contextBuilder.relayState("Relay State Value")
250+
.relyingPartyRegistration(relyingPartyRegistration).build();
251+
Saml2RedirectAuthenticationRequest result = this.factory.createRedirectAuthenticationRequest(context);
252+
assertThat(result.getSamlRequest()).isNotEmpty();
253+
assertThat(result.getRelayState()).isEqualTo("Relay State Value");
254+
assertThat(result.getSigAlg()).isEqualTo(SignatureConstants.ALGO_ID_SIGNATURE_RSA_SHA1);
255+
assertThat(result.getSignature()).isNotNull();
256+
assertThat(result.getBinding()).isEqualTo(Saml2MessageBinding.REDIRECT);
257+
}
258+
243259
private AuthnRequest getAuthNRequest(Saml2MessageBinding binding) {
244260
AbstractSaml2AuthenticationRequest result = (binding == Saml2MessageBinding.REDIRECT)
245261
? this.factory.createRedirectAuthenticationRequest(this.context)

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

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -205,11 +205,12 @@ static BasicCredential getBasicCredential(Saml2X509Credential credential) {
205205
return CredentialSupport.getSimpleCredential(credential.getCertificate(), credential.getPrivateKey());
206206
}
207207

208-
static <T extends SignableSAMLObject> T signed(T signable, Saml2X509Credential credential, String entityId) {
208+
static <T extends SignableSAMLObject> T signed(T signable, Saml2X509Credential credential, String entityId,
209+
String signAlgorithmUri) {
209210
SignatureSigningParameters parameters = new SignatureSigningParameters();
210211
Credential signingCredential = getSigningCredential(credential, entityId);
211212
parameters.setSigningCredential(signingCredential);
212-
parameters.setSignatureAlgorithm(SignatureConstants.ALGO_ID_SIGNATURE_RSA_SHA256);
213+
parameters.setSignatureAlgorithm(signAlgorithmUri);
213214
parameters.setSignatureReferenceDigestMethod(SignatureConstants.ALGO_ID_DIGEST_SHA256);
214215
parameters.setSignatureCanonicalizationAlgorithm(SignatureConstants.ALGO_ID_C14N_EXCL_OMIT_COMMENTS);
215216
try {
@@ -221,6 +222,10 @@ static <T extends SignableSAMLObject> T signed(T signable, Saml2X509Credential c
221222
return signable;
222223
}
223224

225+
static <T extends SignableSAMLObject> T signed(T signable, Saml2X509Credential credential, String entityId) {
226+
return signed(signable, credential, entityId, SignatureConstants.ALGO_ID_SIGNATURE_RSA_SHA256);
227+
}
228+
224229
static EncryptedAssertion encrypted(Assertion assertion, Saml2X509Credential credential) {
225230
X509Certificate certificate = credential.getCertificate();
226231
Encrypter encrypter = getEncrypter(certificate);

0 commit comments

Comments
 (0)