Skip to content

Commit bb95d19

Browse files
committed
Add RelyingPartyRegistrationsDecoder
This adds the RelyingPartyRegistrationsDecoder component which allows configuration with signature verification credentials. Closes spring-projectsgh-12116 Closes spring-projectsgh-15017 Closes spring-projectsgh-15090
1 parent 5475c6c commit bb95d19

File tree

9 files changed

+512
-46
lines changed

9 files changed

+512
-46
lines changed

docs/modules/ROOT/pages/servlet/saml2/login/overview.adoc

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -588,6 +588,59 @@ class MyCustomSecurityConfiguration {
588588
A relying party can be multi-tenant by registering more than one relying party in the `RelyingPartyRegistrationRepository`.
589589
====
590590

591+
In the event that the location you are using is an HTTP url, you should also verify the metadata's signature. You can do this by using ``RelyingPartyRegistrations``'s underlying `RelyingPartyRegistrationsDecoder` like so:
592+
593+
.Relying Party Registrations Decoder
594+
[tabs]
595+
======
596+
Java::
597+
+
598+
[source,java,role="primary"]
599+
----
600+
@Configuration
601+
@EnableWebSecurity
602+
public class MyCustomSecurityConfiguration {
603+
@Bean
604+
RelyingPartyRegistrationsDecoder registrationsDecoder() {
605+
PublicKey pub = lookupAssertingPartyPublicKey();
606+
Set<Credential> credentials = Set.of(new BasicCredential(pub));
607+
return new OpenSamlRelyingPartyRegistrationsDecoder(credentials);
608+
}
609+
610+
@Bean
611+
public RelyingPartyRegistrationRepository registrations(RelyingPartyRegistrationsDecoder decoder) {
612+
return new InMemoryRelyingPartyRegistrationRepository(
613+
decoder.decode("https://idp.example.org/ap/metadata").registrationId("ap").build());
614+
}
615+
}
616+
----
617+
618+
Kotlin::
619+
+
620+
[source,kotlin,role="secondary"]
621+
----
622+
@Configuration
623+
@EnableWebSecurity
624+
class MyCustomSecurityConfiguration {
625+
@Bean
626+
fun registrationsDecoder(): RelyingPartyRegistrationsDecoder {
627+
val pub: PublicKey = lookupAssertingPartyPublicKey()
628+
val credentials: Set<Credential> = java.util.Set.of<Credential>(BasicCredential(pub))
629+
return OpenSamlRelyingPartyRegistrationsDecoder(credentials)
630+
}
631+
632+
@Bean
633+
fun registrations(decoder:RelyingPartyRegistrationsDecoder): RelyingPartyRegistrationRepository {
634+
return InMemoryRelyingPartyRegistrationRepository(
635+
decoder.decode("https://idp.example.org/ap/metadata").registrationId("ap").build())
636+
}
637+
}
638+
----
639+
======
640+
641+
[NOTE]
642+
The semantics of `RelyingPartyRegistrationsDecoder` are identical to `RelyingPartyRegistrations` with two important exceptions. First, the default registration id is URL-safe. Second, once each `RelyingPartyRegistration` is built, it is not of type `OpenSamlRelyingPartyRegistration` as this class is now deprecated.
643+
591644
[[servlet-saml2login-relyingpartyregistration]]
592645
== RelyingPartyRegistration
593646
A {security-api-url}org/springframework/security/saml2/provider/service/registration/RelyingPartyRegistration.html[`RelyingPartyRegistration`]

saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/metadata/OpenSamlSigningUtils.java

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,13 @@ static String serialize(XMLObject object) {
8080
}
8181

8282
static <O extends SignableXMLObject> O sign(O object, RelyingPartyRegistration relyingPartyRegistration) {
83-
SignatureSigningParameters parameters = resolveSigningParameters(relyingPartyRegistration);
83+
List<String> algorithms = relyingPartyRegistration.getAssertingPartyDetails().getSigningAlgorithms();
84+
List<Credential> credentials = resolveSigningCredentials(relyingPartyRegistration);
85+
return sign(object, algorithms, credentials);
86+
}
87+
88+
static <O extends SignableXMLObject> O sign(O object, List<String> algorithms, List<Credential> credentials) {
89+
SignatureSigningParameters parameters = resolveSigningParameters(algorithms, credentials);
8490
try {
8591
SignatureSupport.signObject(object, parameters);
8692
return object;
@@ -98,6 +104,11 @@ private static SignatureSigningParameters resolveSigningParameters(
98104
RelyingPartyRegistration relyingPartyRegistration) {
99105
List<Credential> credentials = resolveSigningCredentials(relyingPartyRegistration);
100106
List<String> algorithms = relyingPartyRegistration.getAssertingPartyDetails().getSigningAlgorithms();
107+
return resolveSigningParameters(algorithms, credentials);
108+
}
109+
110+
private static SignatureSigningParameters resolveSigningParameters(List<String> algorithms,
111+
List<Credential> credentials) {
101112
List<String> digests = Collections.singletonList(SignatureConstants.ALGO_ID_DIGEST_SHA256);
102113
String canonicalization = SignatureConstants.ALGO_ID_C14N_EXCL_OMIT_COMMENTS;
103114
SignatureSigningParametersResolver resolver = new SAMLMetadataSignatureSigningParametersResolver();

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

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2023 the original author or authors.
2+
* Copyright 2002-2024 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -20,6 +20,8 @@
2020
import java.util.Arrays;
2121
import java.util.List;
2222

23+
import org.opensaml.saml.saml2.metadata.EntityDescriptor;
24+
2325
import org.springframework.http.HttpInputMessage;
2426
import org.springframework.http.HttpOutputMessage;
2527
import org.springframework.http.MediaType;
@@ -62,13 +64,15 @@ public class OpenSamlRelyingPartyRegistrationBuilderHttpMessageConverter
6264
OpenSamlInitializationService.initialize();
6365
}
6466

65-
private final OpenSamlMetadataRelyingPartyRegistrationConverter converter;
67+
private final OpenSamlRelyingPartyRegistrationsDecoder converter;
6668

6769
/**
6870
* Creates a {@link OpenSamlRelyingPartyRegistrationBuilderHttpMessageConverter}
6971
*/
7072
public OpenSamlRelyingPartyRegistrationBuilderHttpMessageConverter() {
71-
this.converter = new OpenSamlMetadataRelyingPartyRegistrationConverter();
73+
this.converter = new OpenSamlRelyingPartyRegistrationsDecoder();
74+
this.converter.setRegistrationIdGenerator(EntityDescriptor::getEntityID);
75+
this.converter.setBuilderFactory(OpenSamlRelyingPartyRegistration.Builder::new);
7276
}
7377

7478
@Override
@@ -89,7 +93,7 @@ public List<MediaType> getSupportedMediaTypes() {
8993
@Override
9094
public RelyingPartyRegistration.Builder read(Class<? extends RelyingPartyRegistration.Builder> clazz,
9195
HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException {
92-
return this.converter.convert(inputMessage.getBody()).iterator().next();
96+
return this.converter.decode(inputMessage.getBody()).iterator().next();
9397
}
9498

9599
@Override

0 commit comments

Comments
 (0)