Refactored saml code
This commit is contained in:
@@ -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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user