From 2e2bb65bce792c2f096653ba7b3799fa44da7516 Mon Sep 17 00:00:00 2001 From: rajesh Date: Tue, 24 Sep 2024 19:32:37 +0530 Subject: [PATCH] Refactored saml code --- .../tendermanagement/config/SamlConfig.java | 197 ++++++++++++ .../config/SamlFailureHandler.java | 45 +++ .../config/SamlSuccessHandler.java | 64 ++++ .../config/SecurityConfig.java | 282 +----------------- 4 files changed, 315 insertions(+), 273 deletions(-) create mode 100644 src/main/java/net/gepafin/tendermanagement/config/SamlConfig.java create mode 100644 src/main/java/net/gepafin/tendermanagement/config/SamlFailureHandler.java create mode 100644 src/main/java/net/gepafin/tendermanagement/config/SamlSuccessHandler.java diff --git a/src/main/java/net/gepafin/tendermanagement/config/SamlConfig.java b/src/main/java/net/gepafin/tendermanagement/config/SamlConfig.java new file mode 100644 index 00000000..02a0ae27 --- /dev/null +++ b/src/main/java/net/gepafin/tendermanagement/config/SamlConfig.java @@ -0,0 +1,197 @@ +package net.gepafin.tendermanagement.config; + +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.security.KeyFactory; +import java.security.PrivateKey; +import java.security.cert.CertificateFactory; +import java.security.cert.X509Certificate; +import java.security.spec.PKCS8EncodedKeySpec; +import java.time.Instant; +import java.util.UUID; + +import org.bouncycastle.util.io.pem.PemReader; +import org.opensaml.core.xml.config.XMLObjectProviderRegistrySupport; +import org.opensaml.saml.common.SAMLVersion; +import org.opensaml.saml.common.xml.SAMLConstants; +import org.opensaml.saml.saml2.core.AuthnContextClassRef; +import org.opensaml.saml.saml2.core.AuthnContextComparisonTypeEnumeration; +import org.opensaml.saml.saml2.core.AuthnRequest; +import org.opensaml.saml.saml2.core.RequestedAuthnContext; +import org.opensaml.saml.saml2.core.impl.AuthnContextClassRefBuilder; +import org.opensaml.saml.saml2.core.impl.RequestedAuthnContextBuilder; +import org.opensaml.security.x509.BasicX509Credential; +import org.opensaml.xmlsec.config.impl.DefaultSecurityConfigurationBootstrap; +import org.opensaml.xmlsec.signature.Signature; +import org.opensaml.xmlsec.signature.support.SignatureConstants; +import org.opensaml.xmlsec.signature.support.Signer; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.saml2.core.Saml2X509Credential; +import org.springframework.security.saml2.provider.service.registration.InMemoryRelyingPartyRegistrationRepository; +import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration; +import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistrationRepository; +import org.springframework.security.saml2.provider.service.registration.Saml2MessageBinding; +import org.springframework.security.saml2.provider.service.web.DefaultRelyingPartyRegistrationResolver; +import org.springframework.security.saml2.provider.service.web.RelyingPartyRegistrationResolver; +import org.springframework.security.saml2.provider.service.web.authentication.OpenSaml4AuthenticationRequestResolver; +import org.springframework.security.saml2.provider.service.web.authentication.Saml2AuthenticationRequestResolver; + +@Configuration +public class SamlConfig { + + private final Logger logger = LoggerFactory.getLogger(SamlConfig.class); + + @Value("${base-url}") + String baseUrl; + + @Bean + public RelyingPartyRegistrationRepository relyingPartyRegistrationRepository() { + + String entityId = baseUrl + "/v1/saml/gw/metadata"; + String acsUrl = baseUrl + "/login/saml2/sso/loginumbria"; + + RelyingPartyRegistration registration = RelyingPartyRegistration.withRegistrationId("loginumbria") + .entityId(entityId) + .signingX509Credentials(credentials -> { + try { + credentials.add(Saml2X509Credential.signing(readPrivateKey(), readCertificate())); + } catch (Exception e) { + e.printStackTrace(); + } + }) + .assertionConsumerServiceLocation(acsUrl) + .assertingPartyDetails(details -> details.entityId("https://federatest.umbriadigitale.it/gw/metadata") + .singleSignOnServiceLocation("https://federatest.umbriadigitale.it/gw/SSOProxy/SAML2") + .singleSignOnServiceBinding(Saml2MessageBinding.POST) + .wantAuthnRequestsSigned(true) + .verificationX509Credentials(credentials -> { + try { + // Load the IDP's public certificate for verifying the SAML response signature + credentials.add(Saml2X509Credential.verification(readIdpCertificate())); + } catch (Exception e) { + e.printStackTrace(); + } + }) + ) + .build(); + + return new InMemoryRelyingPartyRegistrationRepository(registration); + } + + public AuthnRequest createSignedAuthnRequest(PrivateKey privateKey, X509Certificate certificate) throws Exception { + AuthnRequest authnRequest = (AuthnRequest) XMLObjectProviderRegistrySupport.getBuilderFactory() + .getBuilder(AuthnRequest.DEFAULT_ELEMENT_NAME) + .buildObject(AuthnRequest.DEFAULT_ELEMENT_NAME); + + authnRequest.setID("_" + UUID.randomUUID().toString()); + authnRequest.setVersion(SAMLVersion.VERSION_20); + // authnRequest.setIssueInstant(new DateTime()); + authnRequest.setIssueInstant(Instant.now()); + + + // Sign the AuthnRequest + // BasicCredential signingCredential = new BasicCredential(certificate, privateKey); + BasicX509Credential signingCredential = new BasicX509Credential(certificate, privateKey); + + Signature signature = (Signature) XMLObjectProviderRegistrySupport.getBuilderFactory() + .getBuilder(Signature.DEFAULT_ELEMENT_NAME) + .buildObject(Signature.DEFAULT_ELEMENT_NAME); + + signature.setCanonicalizationAlgorithm(SignatureConstants.ALGO_ID_C14N_EXCL_OMIT_COMMENTS); + signature.setSigningCredential(signingCredential); + signature.setSignatureAlgorithm(SignatureConstants.ALGO_ID_SIGNATURE_RSA_SHA1); // Set RSA-SHA1 + + authnRequest.setSignature(signature); + DefaultSecurityConfigurationBootstrap.buildDefaultSignatureSigningConfiguration(); + + // Marshall and sign the object + XMLObjectProviderRegistrySupport.getMarshallerFactory().getMarshaller(authnRequest).marshall(authnRequest); + Signer.signObject(signature); + + return authnRequest; + } + +@Bean +public Saml2AuthenticationRequestResolver authenticationRequestResolver(RelyingPartyRegistrationRepository registrations) { + RelyingPartyRegistrationResolver registrationResolver = new DefaultRelyingPartyRegistrationResolver(registrations); + OpenSaml4AuthenticationRequestResolver authenticationRequestResolver = new OpenSaml4AuthenticationRequestResolver(registrationResolver); + + authenticationRequestResolver.setAuthnRequestCustomizer((context) -> { + // Set the required attributes + AuthnRequest authnRequest = context.getAuthnRequest(); + authnRequest.setID("_" + UUID.randomUUID().toString()); // Add a unique ID + authnRequest.setVersion(SAMLVersion.VERSION_20); // Ensure version is 2.0 + authnRequest.setProtocolBinding(SAMLConstants.SAML2_POST_BINDING_URI); // HTTP-POST + + // Set Authentication Context + authnRequest.setRequestedAuthnContext(buildRequestedAuthnContext()); + + // Log the SAML AuthnRequest after setting context + String samlRequest = SamlRequestLogger.convertSAMLObjectToString(authnRequest); + logger.info("SAML AuthnRequest after setting context: " + samlRequest); + }); + + return authenticationRequestResolver; +} + +private RequestedAuthnContext buildRequestedAuthnContext() { + AuthnContextClassRefBuilder authnContextClassRefBuilder = new AuthnContextClassRefBuilder(); + AuthnContextClassRef authnContextClassRef = authnContextClassRefBuilder.buildObject( + SAMLConstants.SAML20_NS, AuthnContextClassRef.DEFAULT_ELEMENT_LOCAL_NAME, SAMLConstants.SAML20_PREFIX + ); + // Set the SPID Level 2 authentication context + authnContextClassRef.setURI("urn:oasis:names:tc:SAML:2.0:ac:classes:SecureRemotePassword"); + + RequestedAuthnContextBuilder requestedAuthnContextBuilder = new RequestedAuthnContextBuilder(); + RequestedAuthnContext requestedAuthnContext = requestedAuthnContextBuilder.buildObject(); + requestedAuthnContext.setComparison(AuthnContextComparisonTypeEnumeration.EXACT); + requestedAuthnContext.getAuthnContextClassRefs().add(authnContextClassRef); + + return requestedAuthnContext; +} + + public PrivateKey readPrivateKey() throws Exception { + // Path to your private key PEM file + try (PemReader pemReader = new PemReader(new InputStreamReader(readKey("dev/saml/private-key.pem")))) { + // Read the PEM content + byte[] pemContent = pemReader.readPemObject().getContent(); + // Decode the PEM content + PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(pemContent); + KeyFactory keyFactory = KeyFactory.getInstance("RSA"); // Use RSA algorithm + // Generate and return the PrivateKey + return keyFactory.generatePrivate(keySpec); + } + } + public X509Certificate readCertificate() throws Exception { + // Path to your certificate PEM fileFile + try (InputStream inStream = readKey("dev/saml/public-cert.pem")) { + CertificateFactory certFactory = CertificateFactory.getInstance("X.509"); + return (X509Certificate) certFactory.generateCertificate(inStream); + } + } + + public X509Certificate readIdpCertificate() throws Exception { + // Path to your IDP public certificate PEM file + try (InputStream inStream = readKey("dev/saml/idp-certificate.pem")) { + CertificateFactory certFactory = CertificateFactory.getInstance("X.509"); + return (X509Certificate) certFactory.generateCertificate(inStream); + } + } + + + public InputStream readKey(String path) throws IOException { + ClassLoader classLoader = getClass().getClassLoader(); + InputStream inputStream = classLoader.getResourceAsStream(path); + + if (inputStream == null) { + throw new FileNotFoundException("file not found : "+path); + } + return inputStream; + } +} diff --git a/src/main/java/net/gepafin/tendermanagement/config/SamlFailureHandler.java b/src/main/java/net/gepafin/tendermanagement/config/SamlFailureHandler.java new file mode 100644 index 00000000..da5f3a65 --- /dev/null +++ b/src/main/java/net/gepafin/tendermanagement/config/SamlFailureHandler.java @@ -0,0 +1,45 @@ +package net.gepafin.tendermanagement.config; + +import java.io.IOException; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.core.AuthenticationException; +import org.springframework.security.web.authentication.AuthenticationFailureHandler; +import org.springframework.stereotype.Component; + +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import net.gepafin.tendermanagement.entities.SamlResponseLogEntity; +import net.gepafin.tendermanagement.repositories.SamlResponseLogRepository; + +@Component +public class SamlFailureHandler implements AuthenticationFailureHandler { + + private final Logger logger = LoggerFactory.getLogger(SamlSuccessHandler.class); + + @Autowired + private SamlResponseLogRepository samlResponseLogRepository; + + @Override + public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, + AuthenticationException exception) throws IOException { + try { + logger.error("SAML login failed: " + exception.getMessage()); + + // Log the failure details to the database (Optional) + SamlResponseLogEntity samlResponseLogEntity = new SamlResponseLogEntity(); + samlResponseLogEntity.setRequest(request.toString()); + samlResponseLogEntity.setResponse(response.toString()); + samlResponseLogEntity.setExceptionObject(exception.toString()); + samlResponseLogRepository.save(samlResponseLogEntity); + + // Handle failure redirection + response.sendRedirect("http://gepafin-staging-fe.s3-website.eu-central-1.amazonaws.com/login"); + } catch (Exception e) { + logger.error("Error processing SAML failure handler", e); + } + } + +} diff --git a/src/main/java/net/gepafin/tendermanagement/config/SamlSuccessHandler.java b/src/main/java/net/gepafin/tendermanagement/config/SamlSuccessHandler.java new file mode 100644 index 00000000..ef7c0bb7 --- /dev/null +++ b/src/main/java/net/gepafin/tendermanagement/config/SamlSuccessHandler.java @@ -0,0 +1,64 @@ +package net.gepafin.tendermanagement.config; + +import java.io.IOException; +import java.util.List; +import java.util.Map; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.core.Authentication; +import org.springframework.security.saml2.provider.service.authentication.Saml2AuthenticatedPrincipal; +import org.springframework.security.saml2.provider.service.authentication.Saml2Authentication; +import org.springframework.security.web.authentication.AuthenticationSuccessHandler; +import org.springframework.stereotype.Component; + +import com.fasterxml.jackson.databind.ObjectMapper; + +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import net.gepafin.tendermanagement.entities.SamlResponseLogEntity; +import net.gepafin.tendermanagement.repositories.SamlResponseLogRepository; + +@Component +public class SamlSuccessHandler implements AuthenticationSuccessHandler{ + + + private final Logger logger = LoggerFactory.getLogger(SamlSuccessHandler.class); + + @Autowired + private SamlResponseLogRepository samlResponseLogRepository; + + @Override + public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, + Authentication authentication) throws IOException { + try { + // Cast the authentication object to Saml2Authentication + Saml2Authentication samlAuth = (Saml2Authentication) authentication; + Saml2AuthenticatedPrincipal principal = (Saml2AuthenticatedPrincipal) samlAuth.getPrincipal(); + + // Extract the user attributes from the principal + Map> userAttributes = principal.getAttributes(); + + // Log the user attributes for debugging purposes + logger.info("SAML User Attributes: " + userAttributes); + + // Save the authentication details in the database (Optional) + SamlResponseLogEntity samlResponseLogEntity = new SamlResponseLogEntity(); + samlResponseLogEntity.setAuthenticationObject(authentication.toString()); + + // Convert user attributes to JSON and save in DB + ObjectMapper objectMapper = new ObjectMapper(); + String userAttributesJson = objectMapper.writeValueAsString(userAttributes); + samlResponseLogEntity.setAuthenticationObject(userAttributesJson); + samlResponseLogRepository.save(samlResponseLogEntity); + + // Successful login logic + logger.info("SAML login successful for user: " + principal.getName()); + response.sendRedirect("http://gepafin-staging-fe.s3-website.eu-central-1.amazonaws.com/login"); + } catch (Exception e) { + logger.error("Error processing SAML success handler", e); + } + } + +} diff --git a/src/main/java/net/gepafin/tendermanagement/config/SecurityConfig.java b/src/main/java/net/gepafin/tendermanagement/config/SecurityConfig.java index c83cc513..17cf0253 100644 --- a/src/main/java/net/gepafin/tendermanagement/config/SecurityConfig.java +++ b/src/main/java/net/gepafin/tendermanagement/config/SecurityConfig.java @@ -1,39 +1,5 @@ package net.gepafin.tendermanagement.config; - -import java.io.FileNotFoundException; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.security.KeyFactory; -import java.security.PrivateKey; -import java.security.cert.CertificateFactory; -import java.security.cert.X509Certificate; -import java.security.spec.PKCS8EncodedKeySpec; -import java.time.Instant; -import java.util.List; -import java.util.Map; -import java.util.UUID; - -import org.apache.xml.security.Init; -import org.bouncycastle.util.io.pem.PemReader; -import org.opensaml.core.config.InitializationService; -import org.opensaml.core.xml.config.XMLObjectProviderRegistrySupport; -import org.opensaml.saml.common.SAMLVersion; -import org.opensaml.saml.common.xml.SAMLConstants; -import org.opensaml.saml.saml2.core.AuthnContextClassRef; -import org.opensaml.saml.saml2.core.AuthnContextComparisonTypeEnumeration; -import org.opensaml.saml.saml2.core.AuthnRequest; -import org.opensaml.saml.saml2.core.RequestedAuthnContext; -import org.opensaml.saml.saml2.core.impl.AuthnContextClassRefBuilder; -import org.opensaml.saml.saml2.core.impl.RequestedAuthnContextBuilder; -import org.opensaml.security.x509.BasicX509Credential; -import org.opensaml.xmlsec.config.impl.DefaultSecurityConfigurationBootstrap; -import org.opensaml.xmlsec.signature.Signature; -import org.opensaml.xmlsec.signature.support.SignatureConstants; -import org.opensaml.xmlsec.signature.support.Signer; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; @@ -49,17 +15,6 @@ import org.springframework.security.config.annotation.web.configurers.AbstractHt import org.springframework.security.config.http.SessionCreationPolicy; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder; -import org.springframework.security.saml2.core.Saml2X509Credential; -import org.springframework.security.saml2.provider.service.authentication.Saml2AuthenticatedPrincipal; -import org.springframework.security.saml2.provider.service.authentication.Saml2Authentication; -import org.springframework.security.saml2.provider.service.registration.InMemoryRelyingPartyRegistrationRepository; -import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration; -import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistrationRepository; -import org.springframework.security.saml2.provider.service.registration.Saml2MessageBinding; -import org.springframework.security.saml2.provider.service.web.DefaultRelyingPartyRegistrationResolver; -import org.springframework.security.saml2.provider.service.web.RelyingPartyRegistrationResolver; -import org.springframework.security.saml2.provider.service.web.authentication.OpenSaml4AuthenticationRequestResolver; -import org.springframework.security.saml2.provider.service.web.authentication.Saml2AuthenticationRequestResolver; import org.springframework.security.web.SecurityFilterChain; import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; import org.springframework.security.web.servlet.util.matcher.MvcRequestMatcher; @@ -69,63 +24,34 @@ import org.springframework.web.cors.UrlBasedCorsConfigurationSource; import org.springframework.web.filter.CorsFilter; import org.springframework.web.servlet.handler.HandlerMappingIntrospector; -import com.fasterxml.jackson.databind.ObjectMapper; import io.swagger.v3.oas.models.Components; import io.swagger.v3.oas.models.OpenAPI; import io.swagger.v3.oas.models.security.SecurityRequirement; import io.swagger.v3.oas.models.security.SecurityScheme; import io.swagger.v3.oas.models.servers.Server; -import jakarta.annotation.PostConstruct; import net.gepafin.tendermanagement.config.jwt.JWTFilter; import net.gepafin.tendermanagement.config.jwt.TokenProvider; -import net.gepafin.tendermanagement.entities.SamlResponseLogEntity; -import net.gepafin.tendermanagement.repositories.SamlResponseLogRepository; -//import org.springframework.security.saml2.provider.service.authentication.Saml2AuthenticationRequestContext; -//import org.springframework.security.saml2.core.Saml2AuthenticationRequest; + @Configuration @EnableWebSecurity @EnableMethodSecurity(prePostEnabled = true) public class SecurityConfig { - private final Logger logger = LoggerFactory.getLogger(SecurityConfig.class); private final TokenProvider tokenProvider; + private final SamlSuccessHandler samlSuccessHandler; + private final SamlFailureHandler samlFailureHandler; @Value("${base-url}") String baseUrl; @Autowired - private SamlResponseLogRepository samlResponseLogRepository; - - @Autowired - public SecurityConfig(TokenProvider tokenProvider) { + public SecurityConfig(TokenProvider tokenProvider, SamlSuccessHandler samlSuccessHandler, SamlFailureHandler samlFailureHandler) { this.tokenProvider = tokenProvider; + this.samlSuccessHandler =samlSuccessHandler; + this.samlFailureHandler=samlFailureHandler; } - - @PostConstruct - public void initXmlSecurity() throws Exception { - // Initialize Apache XML Security (Santuario) - Init.init(); - } - - - @PostConstruct - public void initOpenSAML() throws Exception { - InitializationService.initialize(); - } - -// -// @Bean -// public Saml2AuthenticationRequestResolver authenticationRequestResolver() { -// return (Saml2AuthenticationRequestContext context) -> { -// Saml2AuthenticationRequest request = Saml2AuthenticationRequest.withAuthenticationRequestContext(context) -// .authenticationContextClassRef("urn:oasis:names:tc:SAML:2.0:ac:classes:SecureRemotePassword") // Add context here -// .build(); -// return request; -// }; -// } - @Bean public AuthenticationManager authenticationManager(AuthenticationConfiguration config) throws Exception { return config.getAuthenticationManager(); @@ -186,55 +112,8 @@ public class SecurityConfig { * .saml2Login(saml -> saml.loginPage("/saml/login") // Entry point for SAML * login .defaultSuccessUrl("/") // Redirect after successful SAML login ); */ - .saml2Login(saml -> - saml.defaultSuccessUrl("/") - .successHandler((request, response, authentication) -> { - try { - // Cast the authentication object to Saml2Authentication - Saml2Authentication samlAuth = (Saml2Authentication) authentication; - Saml2AuthenticatedPrincipal principal = (Saml2AuthenticatedPrincipal) samlAuth.getPrincipal(); - - // Extract the user attributes from the principal - Map> userAttributes = principal.getAttributes(); - - // Log the user attributes for debugging purposes - logger.info("SAML User Attributes: " + userAttributes); - - // Save the authentication details in the database (Optional) - SamlResponseLogEntity samlResponseLogEntity = new SamlResponseLogEntity(); - samlResponseLogEntity.setAuthenticationObject(authentication.toString()); - - // Convert user attributes to JSON and save in DB - ObjectMapper objectMapper = new ObjectMapper(); - String userAttributesJson = objectMapper.writeValueAsString(userAttributes); - samlResponseLogEntity.setAuthenticationObject(userAttributesJson); - samlResponseLogRepository.save(samlResponseLogEntity); - - // Successful login logic - logger.info("SAML login successful for user: " + principal.getName()); - response.sendRedirect("http://gepafin-staging-fe.s3-website.eu-central-1.amazonaws.com/login"); - } catch (Exception e) { - logger.error("Error processing SAML success handler", e); - } - }) - .failureHandler((request, response, exception) -> { - try { - logger.error("SAML login failed: " + exception.getMessage()); - - // Log the failure details to the database (Optional) - SamlResponseLogEntity samlResponseLogEntity = new SamlResponseLogEntity(); - samlResponseLogEntity.setRequest(request.toString()); - samlResponseLogEntity.setResponse(response.toString()); - samlResponseLogEntity.setExceptionObject(exception.toString()); - samlResponseLogRepository.save(samlResponseLogEntity); - - // Handle failure redirection - response.sendRedirect("http://gepafin-staging-fe.s3-website.eu-central-1.amazonaws.com/login"); - } catch (Exception e) { - logger.error("Error processing SAML failure handler", e); - } - }) - ); + .saml2Login(saml -> saml.defaultSuccessUrl("/").successHandler(samlSuccessHandler) + .failureHandler(samlFailureHandler)); return http.build(); @@ -250,148 +129,5 @@ public class SecurityConfig { .scheme("bearer").bearerFormat("JWT"))); } - @Bean - public RelyingPartyRegistrationRepository relyingPartyRegistrationRepository() { - - String entityId = baseUrl + "/v1/saml/gw/metadata"; - String acsUrl = baseUrl + "/login/saml2/sso/loginumbria"; - - RelyingPartyRegistration registration = RelyingPartyRegistration.withRegistrationId("loginumbria") - .entityId(entityId) - .signingX509Credentials(credentials -> { - try { - credentials.add(Saml2X509Credential.signing(readPrivateKey(), readCertificate())); - } catch (Exception e) { - e.printStackTrace(); - } - }) - .assertionConsumerServiceLocation(acsUrl) - .assertingPartyDetails(details -> details.entityId("https://federatest.umbriadigitale.it/gw/metadata") - .singleSignOnServiceLocation("https://federatest.umbriadigitale.it/gw/SSOProxy/SAML2") - .singleSignOnServiceBinding(Saml2MessageBinding.POST) - .wantAuthnRequestsSigned(true) - .verificationX509Credentials(credentials -> { - try { - // Load the IDP's public certificate for verifying the SAML response signature - credentials.add(Saml2X509Credential.verification(readIdpCertificate())); - } catch (Exception e) { - e.printStackTrace(); - } - }) - ) - .build(); - - return new InMemoryRelyingPartyRegistrationRepository(registration); - } - - public AuthnRequest createSignedAuthnRequest(PrivateKey privateKey, X509Certificate certificate) throws Exception { - AuthnRequest authnRequest = (AuthnRequest) XMLObjectProviderRegistrySupport.getBuilderFactory() - .getBuilder(AuthnRequest.DEFAULT_ELEMENT_NAME) - .buildObject(AuthnRequest.DEFAULT_ELEMENT_NAME); - - authnRequest.setID("_" + UUID.randomUUID().toString()); - authnRequest.setVersion(SAMLVersion.VERSION_20); - // authnRequest.setIssueInstant(new DateTime()); - authnRequest.setIssueInstant(Instant.now()); - - - // Sign the AuthnRequest - // BasicCredential signingCredential = new BasicCredential(certificate, privateKey); - BasicX509Credential signingCredential = new BasicX509Credential(certificate, privateKey); - - Signature signature = (Signature) XMLObjectProviderRegistrySupport.getBuilderFactory() - .getBuilder(Signature.DEFAULT_ELEMENT_NAME) - .buildObject(Signature.DEFAULT_ELEMENT_NAME); - - signature.setCanonicalizationAlgorithm(SignatureConstants.ALGO_ID_C14N_EXCL_OMIT_COMMENTS); - signature.setSigningCredential(signingCredential); - signature.setSignatureAlgorithm(SignatureConstants.ALGO_ID_SIGNATURE_RSA_SHA1); // Set RSA-SHA1 - - authnRequest.setSignature(signature); - DefaultSecurityConfigurationBootstrap.buildDefaultSignatureSigningConfiguration(); - - // Marshall and sign the object - XMLObjectProviderRegistrySupport.getMarshallerFactory().getMarshaller(authnRequest).marshall(authnRequest); - Signer.signObject(signature); - - return authnRequest; - } - -@Bean -public Saml2AuthenticationRequestResolver authenticationRequestResolver(RelyingPartyRegistrationRepository registrations) { - RelyingPartyRegistrationResolver registrationResolver = new DefaultRelyingPartyRegistrationResolver(registrations); - OpenSaml4AuthenticationRequestResolver authenticationRequestResolver = new OpenSaml4AuthenticationRequestResolver(registrationResolver); - - authenticationRequestResolver.setAuthnRequestCustomizer((context) -> { - // Set the required attributes - AuthnRequest authnRequest = context.getAuthnRequest(); - authnRequest.setID("_" + UUID.randomUUID().toString()); // Add a unique ID - authnRequest.setVersion(SAMLVersion.VERSION_20); // Ensure version is 2.0 - authnRequest.setProtocolBinding(SAMLConstants.SAML2_POST_BINDING_URI); // HTTP-POST - - // Set Authentication Context - authnRequest.setRequestedAuthnContext(buildRequestedAuthnContext()); - - // Log the SAML AuthnRequest after setting context - String samlRequest = SamlRequestLogger.convertSAMLObjectToString(authnRequest); - logger.info("SAML AuthnRequest after setting context: " + samlRequest); - }); - - return authenticationRequestResolver; -} - -private RequestedAuthnContext buildRequestedAuthnContext() { - AuthnContextClassRefBuilder authnContextClassRefBuilder = new AuthnContextClassRefBuilder(); - AuthnContextClassRef authnContextClassRef = authnContextClassRefBuilder.buildObject( - SAMLConstants.SAML20_NS, AuthnContextClassRef.DEFAULT_ELEMENT_LOCAL_NAME, SAMLConstants.SAML20_PREFIX - ); - // Set the SPID Level 2 authentication context - authnContextClassRef.setURI("urn:oasis:names:tc:SAML:2.0:ac:classes:SecureRemotePassword"); - - RequestedAuthnContextBuilder requestedAuthnContextBuilder = new RequestedAuthnContextBuilder(); - RequestedAuthnContext requestedAuthnContext = requestedAuthnContextBuilder.buildObject(); - requestedAuthnContext.setComparison(AuthnContextComparisonTypeEnumeration.EXACT); - requestedAuthnContext.getAuthnContextClassRefs().add(authnContextClassRef); - - return requestedAuthnContext; -} - - public PrivateKey readPrivateKey() throws Exception { - // Path to your private key PEM file - try (PemReader pemReader = new PemReader(new InputStreamReader(readKey("dev/saml/private-key.pem")))) { - // Read the PEM content - byte[] pemContent = pemReader.readPemObject().getContent(); - // Decode the PEM content - PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(pemContent); - KeyFactory keyFactory = KeyFactory.getInstance("RSA"); // Use RSA algorithm - // Generate and return the PrivateKey - return keyFactory.generatePrivate(keySpec); - } - } - public X509Certificate readCertificate() throws Exception { - // Path to your certificate PEM fileFile - try (InputStream inStream = readKey("dev/saml/public-cert.pem")) { - CertificateFactory certFactory = CertificateFactory.getInstance("X.509"); - return (X509Certificate) certFactory.generateCertificate(inStream); - } - } - - public X509Certificate readIdpCertificate() throws Exception { - // Path to your IDP public certificate PEM file - try (InputStream inStream = readKey("dev/saml/idp-certificate.pem")) { - CertificateFactory certFactory = CertificateFactory.getInstance("X.509"); - return (X509Certificate) certFactory.generateCertificate(inStream); - } - } - - - public InputStream readKey(String path) throws IOException { - ClassLoader classLoader = getClass().getClassLoader(); - InputStream inputStream = classLoader.getResourceAsStream(path); - - if (inputStream == null) { - throw new FileNotFoundException("file not found : "+path); - } - return inputStream; - } + } \ No newline at end of file