summaryrefslogtreecommitdiff
path: root/eaaf_modules/eaaf_module_pvp2_core/src/main/java/at/gv/egiz/eaaf/modules/pvp2/impl/builder/PvpMetadataBuilder.java
diff options
context:
space:
mode:
Diffstat (limited to 'eaaf_modules/eaaf_module_pvp2_core/src/main/java/at/gv/egiz/eaaf/modules/pvp2/impl/builder/PvpMetadataBuilder.java')
-rw-r--r--eaaf_modules/eaaf_module_pvp2_core/src/main/java/at/gv/egiz/eaaf/modules/pvp2/impl/builder/PvpMetadataBuilder.java458
1 files changed, 458 insertions, 0 deletions
diff --git a/eaaf_modules/eaaf_module_pvp2_core/src/main/java/at/gv/egiz/eaaf/modules/pvp2/impl/builder/PvpMetadataBuilder.java b/eaaf_modules/eaaf_module_pvp2_core/src/main/java/at/gv/egiz/eaaf/modules/pvp2/impl/builder/PvpMetadataBuilder.java
new file mode 100644
index 00000000..1efa8745
--- /dev/null
+++ b/eaaf_modules/eaaf_module_pvp2_core/src/main/java/at/gv/egiz/eaaf/modules/pvp2/impl/builder/PvpMetadataBuilder.java
@@ -0,0 +1,458 @@
+/*
+ * Copyright 2017 Graz University of Technology EAAF-Core Components has been developed in a
+ * cooperation between EGIZ, A-SIT Plus, A-SIT, and Graz University of Technology.
+ *
+ * Licensed under the EUPL, Version 1.2 or - as soon they will be approved by the European
+ * Commission - subsequent versions of the EUPL (the "Licence"); You may not use this work except in
+ * compliance with the Licence. You may obtain a copy of the Licence at:
+ * https://joinup.ec.europa.eu/news/understanding-eupl-v12
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the Licence
+ * is distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the Licence for the specific language governing permissions and limitations under
+ * the Licence.
+ *
+ * This product combines work with different licenses. See the "NOTICE" text file for details on the
+ * various modules and licenses. The "NOTICE" text file is part of the distribution. Any derivative
+ * works that you distribute must include a readable copy of the "NOTICE" text file.
+*/
+
+package at.gv.egiz.eaaf.modules.pvp2.impl.builder;
+
+import java.io.IOException;
+import java.io.StringWriter;
+import java.util.Collection;
+import java.util.List;
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerException;
+import javax.xml.transform.TransformerFactory;
+import javax.xml.transform.TransformerFactoryConfigurationError;
+import javax.xml.transform.dom.DOMSource;
+import javax.xml.transform.stream.StreamResult;
+import at.gv.egiz.eaaf.core.exceptions.EaafException;
+import at.gv.egiz.eaaf.modules.pvp2.api.metadata.IPvpMetadataBuilderConfiguration;
+import at.gv.egiz.eaaf.modules.pvp2.impl.opensaml.initialize.EaafDefaultSaml2Bootstrap;
+import at.gv.egiz.eaaf.modules.pvp2.impl.utils.AbstractCredentialProvider;
+import at.gv.egiz.eaaf.modules.pvp2.impl.utils.Saml2Utils;
+import org.apache.commons.httpclient.auth.CredentialsNotAvailableException;
+import org.apache.commons.lang3.StringUtils;
+import org.joda.time.DateTime;
+import org.opensaml.common.xml.SAMLConstants;
+import org.opensaml.saml2.metadata.AssertionConsumerService;
+import org.opensaml.saml2.metadata.AttributeConsumingService;
+import org.opensaml.saml2.metadata.ContactPerson;
+import org.opensaml.saml2.metadata.EntitiesDescriptor;
+import org.opensaml.saml2.metadata.EntityDescriptor;
+import org.opensaml.saml2.metadata.IDPSSODescriptor;
+import org.opensaml.saml2.metadata.KeyDescriptor;
+import org.opensaml.saml2.metadata.LocalizedString;
+import org.opensaml.saml2.metadata.NameIDFormat;
+import org.opensaml.saml2.metadata.Organization;
+import org.opensaml.saml2.metadata.RequestedAttribute;
+import org.opensaml.saml2.metadata.RoleDescriptor;
+import org.opensaml.saml2.metadata.SPSSODescriptor;
+import org.opensaml.saml2.metadata.ServiceName;
+import org.opensaml.saml2.metadata.SingleLogoutService;
+import org.opensaml.saml2.metadata.SingleSignOnService;
+import org.opensaml.xml.io.Marshaller;
+import org.opensaml.xml.io.MarshallingException;
+import org.opensaml.xml.security.SecurityException;
+import org.opensaml.xml.security.SecurityHelper;
+import org.opensaml.xml.security.credential.Credential;
+import org.opensaml.xml.security.credential.UsageType;
+import org.opensaml.xml.security.keyinfo.KeyInfoGenerator;
+import org.opensaml.xml.security.x509.X509KeyInfoGeneratorFactory;
+import org.opensaml.xml.signature.Signature;
+import org.opensaml.xml.signature.SignatureException;
+import org.opensaml.xml.signature.Signer;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Service;
+import org.w3c.dom.Document;
+
+/**
+ * PVP metadata builder implementation.
+ *
+ * @author tlenz
+ *
+ */
+
+@Service("PVPMetadataBuilder")
+public class PvpMetadataBuilder {
+
+ private static final Logger log = LoggerFactory.getLogger(PvpMetadataBuilder.class);
+
+ X509KeyInfoGeneratorFactory keyInfoFactory = null;
+
+ /**
+ * PVP metadata builder.
+ *
+ */
+ public PvpMetadataBuilder() {
+ keyInfoFactory = new X509KeyInfoGeneratorFactory();
+ keyInfoFactory.setEmitEntityIDAsKeyName(true);
+ keyInfoFactory.setEmitEntityCertificate(true);
+
+ }
+
+
+ /**
+ * Build PVP 2.1 conform SAML2 metadata.
+ *
+ * @param config PVPMetadataBuilder configuration*
+ * @return PVP metadata as XML String
+ * @throws SecurityException In case of an error
+ * @throws ConfigurationException In case of an error
+ * @throws CredentialsNotAvailableException In case of an error
+ * @throws TransformerFactoryConfigurationError In case of an error
+ * @throws MarshallingException In case of an error
+ * @throws TransformerException In case of an error
+ * @throws ParserConfigurationException In case of an error
+ * @throws IOException In case of an error
+ * @throws SignatureException In case of an error
+ */
+ public String buildPvpMetadata(final IPvpMetadataBuilderConfiguration config)
+ throws CredentialsNotAvailableException, EaafException, SecurityException,
+ TransformerFactoryConfigurationError, MarshallingException, TransformerException,
+ ParserConfigurationException, IOException, SignatureException {
+ final DateTime date = new DateTime();
+ final EntityDescriptor entityDescriptor = Saml2Utils.createSamlObject(EntityDescriptor.class);
+
+ // set entityID
+ entityDescriptor.setEntityID(config.getEntityID());
+
+ // set contact and organisation information
+ final List<ContactPerson> contactPersons = config.getContactPersonInformation();
+ if (contactPersons != null) {
+ entityDescriptor.getContactPersons().addAll(contactPersons);
+ }
+
+ final Organization organisation = config.getOrgansiationInformation();
+ if (organisation != null) {
+ entityDescriptor.setOrganization(organisation);
+ }
+
+ // set IDP metadata
+ if (config.buildIdpSsoDescriptor()) {
+ final RoleDescriptor idpSsoDesc = generateIdpMetadata(config);
+ if (idpSsoDesc != null) {
+ entityDescriptor.getRoleDescriptors().add(idpSsoDesc);
+ }
+
+ }
+
+ // set SP metadata for interfederation
+ if (config.buildSpSsoDescriptor()) {
+ final RoleDescriptor spSsoDesc = generateSpMetadata(config);
+ if (spSsoDesc != null) {
+ entityDescriptor.getRoleDescriptors().add(spSsoDesc);
+ }
+
+ }
+
+ // set metadata signature parameters
+ final Credential metadataSignCred = config.getMetadataSigningCredentials();
+ final Signature signature = AbstractCredentialProvider.getIdpSignature(metadataSignCred);
+ SecurityHelper.prepareSignatureParams(signature, metadataSignCred, null, null);
+
+ // initialize XML document builder
+ DocumentBuilder builder;
+ final DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
+
+ builder = factory.newDocumentBuilder();
+ final Document document = builder.newDocument();
+
+
+ // build entities descriptor
+ if (config.buildEntitiesDescriptorAsRootElement()) {
+ final EntitiesDescriptor entitiesDescriptor =
+ Saml2Utils.createSamlObject(EntitiesDescriptor.class);
+ entitiesDescriptor.setName(config.getEntityFriendlyName());
+ entitiesDescriptor.setID(Saml2Utils.getSecureIdentifier());
+ entitiesDescriptor.setValidUntil(date.plusHours(config.getMetadataValidUntil()));
+ entitiesDescriptor.getEntityDescriptors().add(entityDescriptor);
+
+ // load default PVP security configurations
+ EaafDefaultSaml2Bootstrap.initializeDefaultPvpConfiguration();
+ entitiesDescriptor.setSignature(signature);
+
+
+ // marshall document
+ final Marshaller out =
+ org.opensaml.xml.Configuration.getMarshallerFactory().getMarshaller(entitiesDescriptor);
+ out.marshall(entitiesDescriptor, document);
+
+ } else {
+ entityDescriptor.setValidUntil(date.plusHours(config.getMetadataValidUntil()));
+ entityDescriptor.setID(Saml2Utils.getSecureIdentifier());
+
+ entityDescriptor.setSignature(signature);
+
+
+
+ // marshall document
+ final Marshaller out =
+ org.opensaml.xml.Configuration.getMarshallerFactory().getMarshaller(entityDescriptor);
+ out.marshall(entityDescriptor, document);
+
+ }
+
+ // sign metadata
+ Signer.signObject(signature);
+
+ // transform metadata object to XML string
+ final Transformer transformer = TransformerFactory.newInstance().newTransformer();
+
+ final StringWriter sw = new StringWriter();
+ final StreamResult sr = new StreamResult(sw);
+ final DOMSource source = new DOMSource(document);
+ transformer.transform(source, sr);
+ sw.close();
+
+ return sw.toString();
+ }
+
+
+ private RoleDescriptor generateSpMetadata(final IPvpMetadataBuilderConfiguration config)
+ throws CredentialsNotAvailableException, SecurityException, EaafException {
+ final SPSSODescriptor spSsoDescriptor = Saml2Utils.createSamlObject(SPSSODescriptor.class);
+ spSsoDescriptor.addSupportedProtocol(SAMLConstants.SAML20P_NS);
+ spSsoDescriptor.setAuthnRequestsSigned(config.wantAuthnRequestSigned());
+ spSsoDescriptor.setWantAssertionsSigned(config.wantAssertionSigned());
+
+ final KeyInfoGenerator keyInfoGenerator = keyInfoFactory.newInstance();
+
+ // Set AuthRequest Signing certificate
+ final Credential authcredential = config.getRequestorResponseSigningCredentials();
+ if (authcredential == null) {
+ log.warn("SP Metadata generation FAILED! --> Builder has NO request signing-credential. ");
+ return null;
+
+ } else {
+ final KeyDescriptor signKeyDescriptor = Saml2Utils.createSamlObject(KeyDescriptor.class);
+ signKeyDescriptor.setUse(UsageType.SIGNING);
+ signKeyDescriptor.setKeyInfo(keyInfoGenerator.generate(authcredential));
+ spSsoDescriptor.getKeyDescriptors().add(signKeyDescriptor);
+
+ }
+
+ // Set assertion encryption credentials
+ final Credential authEncCredential = config.getEncryptionCredentials();
+
+ if (authEncCredential != null) {
+ final KeyDescriptor encryKeyDescriptor = Saml2Utils.createSamlObject(KeyDescriptor.class);
+ encryKeyDescriptor.setUse(UsageType.ENCRYPTION);
+ encryKeyDescriptor.setKeyInfo(keyInfoGenerator.generate(authEncCredential));
+ spSsoDescriptor.getKeyDescriptors().add(encryKeyDescriptor);
+
+ } else {
+ log.warn("No Assertion Encryption-Key defined. This setting is not recommended!");
+
+ }
+
+ // check nameID formates
+ if (config.getSpAllowedNameIdTypes() == null || config.getSpAllowedNameIdTypes().size() == 0) {
+ log.warn(
+ "SP Metadata generation FAILED! --> Builder has NO provideable SAML2 nameIDFormats. ");
+ return null;
+
+ } else {
+ for (final String format : config.getSpAllowedNameIdTypes()) {
+ final NameIDFormat nameIdFormat = Saml2Utils.createSamlObject(NameIDFormat.class);
+ nameIdFormat.setFormat(format);
+ spSsoDescriptor.getNameIDFormats().add(nameIdFormat);
+
+ }
+ }
+
+
+ // add POST-Binding assertion consumer services
+ if (StringUtils.isNotEmpty(config.getSpAssertionConsumerServicePostBindingUrl())) {
+ final AssertionConsumerService postassertionConsumerService =
+ Saml2Utils.createSamlObject(AssertionConsumerService.class);
+ postassertionConsumerService.setIndex(0);
+ postassertionConsumerService.setBinding(SAMLConstants.SAML2_POST_BINDING_URI);
+ postassertionConsumerService
+ .setLocation(config.getSpAssertionConsumerServicePostBindingUrl());
+ postassertionConsumerService.setIsDefault(true);
+ spSsoDescriptor.getAssertionConsumerServices().add(postassertionConsumerService);
+
+ }
+
+ // add POST-Binding assertion consumer services
+ if (StringUtils.isNotEmpty(config.getSpAssertionConsumerServiceRedirectBindingUrl())) {
+ final AssertionConsumerService redirectassertionConsumerService =
+ Saml2Utils.createSamlObject(AssertionConsumerService.class);
+ redirectassertionConsumerService.setIndex(1);
+ redirectassertionConsumerService.setBinding(SAMLConstants.SAML2_REDIRECT_BINDING_URI);
+ redirectassertionConsumerService
+ .setLocation(config.getSpAssertionConsumerServiceRedirectBindingUrl());
+ spSsoDescriptor.getAssertionConsumerServices().add(redirectassertionConsumerService);
+
+ }
+
+ // validate WebSSO endpoints
+ if (spSsoDescriptor.getAssertionConsumerServices().size() == 0) {
+ log.warn(
+ "SP Metadata generation FAILED! --> NO SAML2 AssertionConsumerService endpoint found. ");
+ return null;
+
+ }
+
+ // add POST-Binding SLO descriptor
+ if (StringUtils.isNotEmpty(config.getSpSloPostBindingUrl())) {
+ final SingleLogoutService postSloService =
+ Saml2Utils.createSamlObject(SingleLogoutService.class);
+ postSloService.setLocation(config.getSpSloPostBindingUrl());
+ postSloService.setBinding(SAMLConstants.SAML2_POST_BINDING_URI);
+ spSsoDescriptor.getSingleLogoutServices().add(postSloService);
+
+ }
+
+ // add POST-Binding SLO descriptor
+ if (StringUtils.isNotEmpty(config.getSpSloRedirectBindingUrl())) {
+ final SingleLogoutService redirectSloService =
+ Saml2Utils.createSamlObject(SingleLogoutService.class);
+ redirectSloService.setLocation(config.getSpSloRedirectBindingUrl());
+ redirectSloService.setBinding(SAMLConstants.SAML2_REDIRECT_BINDING_URI);
+ spSsoDescriptor.getSingleLogoutServices().add(redirectSloService);
+
+ }
+
+ // add POST-Binding SLO descriptor
+ if (StringUtils.isNotEmpty(config.getSpSloSoapBindingUrl())) {
+ final SingleLogoutService soapSloService =
+ Saml2Utils.createSamlObject(SingleLogoutService.class);
+ soapSloService.setLocation(config.getSpSloSoapBindingUrl());
+ soapSloService.setBinding(SAMLConstants.SAML2_SOAP11_BINDING_URI);
+ spSsoDescriptor.getSingleLogoutServices().add(soapSloService);
+
+ }
+
+
+ // add required attributes
+ final Collection<RequestedAttribute> reqSpAttr = config.getSpRequiredAttributes();
+ final AttributeConsumingService attributeService =
+ Saml2Utils.createSamlObject(AttributeConsumingService.class);
+
+ attributeService.setIndex(0);
+ attributeService.setIsDefault(true);
+ final ServiceName serviceName = Saml2Utils.createSamlObject(ServiceName.class);
+ serviceName.setName(new LocalizedString("Default Service", "en"));
+ attributeService.getNames().add(serviceName);
+
+ if (reqSpAttr != null && reqSpAttr.size() > 0) {
+ log.debug("Add " + reqSpAttr.size() + " attributes to SP metadata");
+ attributeService.getRequestAttributes().addAll(reqSpAttr);
+
+ } else {
+ log.debug("SP metadata contains NO requested attributes.");
+
+ }
+
+ spSsoDescriptor.getAttributeConsumingServices().add(attributeService);
+
+ return spSsoDescriptor;
+ }
+
+ private IDPSSODescriptor generateIdpMetadata(final IPvpMetadataBuilderConfiguration config)
+ throws EaafException, CredentialsNotAvailableException, SecurityException {
+ // check response signing credential
+ final Credential responseSignCred = config.getRequestorResponseSigningCredentials();
+ if (responseSignCred == null) {
+ log.warn("IDP Metadata generation FAILED! --> Builder has NO Response signing credential. ");
+ return null;
+
+ }
+
+ // check nameID formates
+ if (config.getIdpPossibleNameIdTypes() == null
+ || config.getIdpPossibleNameIdTypes().size() == 0) {
+ log.warn(
+ "IDP Metadata generation FAILED! --> Builder has NO provideable SAML2 nameIDFormats. ");
+ return null;
+
+ }
+
+ // build SAML2 IDP-SSO descriptor element
+ final IDPSSODescriptor idpSsoDescriptor = Saml2Utils.createSamlObject(IDPSSODescriptor.class);
+
+ idpSsoDescriptor.addSupportedProtocol(SAMLConstants.SAML20P_NS);
+
+ // set ass default value, because PVP 2.x specification defines this feature as MUST
+ idpSsoDescriptor.setWantAuthnRequestsSigned(config.wantAuthnRequestSigned());
+
+ // add WebSSO descriptor for POST-Binding
+ if (StringUtils.isNotEmpty(config.getIdpWebSsoPostBindingUrl())) {
+ final SingleSignOnService postSingleSignOnService =
+ Saml2Utils.createSamlObject(SingleSignOnService.class);
+ postSingleSignOnService.setLocation(config.getIdpWebSsoPostBindingUrl());
+ postSingleSignOnService.setBinding(SAMLConstants.SAML2_POST_BINDING_URI);
+ idpSsoDescriptor.getSingleSignOnServices().add(postSingleSignOnService);
+
+ }
+
+ // add WebSSO descriptor for Redirect-Binding
+ if (StringUtils.isNotEmpty(config.getIdpWebSsoRedirectBindingUrl())) {
+ final SingleSignOnService postSingleSignOnService =
+ Saml2Utils.createSamlObject(SingleSignOnService.class);
+ postSingleSignOnService.setLocation(config.getIdpWebSsoRedirectBindingUrl());
+ postSingleSignOnService.setBinding(SAMLConstants.SAML2_REDIRECT_BINDING_URI);
+ idpSsoDescriptor.getSingleSignOnServices().add(postSingleSignOnService);
+
+ }
+
+ // add Single LogOut POST-Binding endpoing
+ if (StringUtils.isNotEmpty(config.getIdpSloPostBindingUrl())) {
+ final SingleLogoutService postSloService =
+ Saml2Utils.createSamlObject(SingleLogoutService.class);
+ postSloService.setLocation(config.getIdpSloPostBindingUrl());
+ postSloService.setBinding(SAMLConstants.SAML2_POST_BINDING_URI);
+ idpSsoDescriptor.getSingleLogoutServices().add(postSloService);
+
+ }
+
+ // add Single LogOut Redirect-Binding endpoing
+ if (StringUtils.isNotEmpty(config.getIdpSloRedirectBindingUrl())) {
+ final SingleLogoutService redirectSloService =
+ Saml2Utils.createSamlObject(SingleLogoutService.class);
+ redirectSloService.setLocation(config.getIdpSloRedirectBindingUrl());
+ redirectSloService.setBinding(SAMLConstants.SAML2_REDIRECT_BINDING_URI);
+ idpSsoDescriptor.getSingleLogoutServices().add(redirectSloService);
+
+ }
+
+ // validate WebSSO endpoints
+ if (idpSsoDescriptor.getSingleSignOnServices().size() == 0) {
+ log.warn("IDP Metadata generation FAILED! --> NO SAML2 SingleSignOnService endpoint found. ");
+ return null;
+
+ }
+
+ // set assertion signing key
+ final KeyDescriptor signKeyDescriptor = Saml2Utils.createSamlObject(KeyDescriptor.class);
+ signKeyDescriptor.setUse(UsageType.SIGNING);
+ final KeyInfoGenerator keyInfoGenerator = keyInfoFactory.newInstance();
+ signKeyDescriptor
+ .setKeyInfo(keyInfoGenerator.generate(config.getRequestorResponseSigningCredentials()));
+ idpSsoDescriptor.getKeyDescriptors().add(signKeyDescriptor);
+
+ // set IDP attribute set
+ idpSsoDescriptor.getAttributes().addAll(config.getIdpPossibleAttributes());
+
+ // set providable nameID formats
+ for (final String format : config.getIdpPossibleNameIdTypes()) {
+ final NameIDFormat nameIdFormat = Saml2Utils.createSamlObject(NameIDFormat.class);
+ nameIdFormat.setFormat(format);
+ idpSsoDescriptor.getNameIDFormats().add(nameIdFormat);
+
+ }
+
+ return idpSsoDescriptor;
+
+ }
+
+}