All Downloads are FREE. Search and download functionalities are using the official Maven repository.

se.idsec.signservice.integration.signmessage.impl.DefaultSignMessageProcessor Maven / Gradle / Ivy

The newest version!
/*
 * Copyright 2019-2023 IDsec Solutions AB
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package se.idsec.signservice.integration.signmessage.impl;

import jakarta.annotation.Nonnull;
import jakarta.annotation.PostConstruct;
import jakarta.xml.bind.JAXBContext;
import jakarta.xml.bind.JAXBException;
import lombok.extern.slf4j.Slf4j;
import net.shibboleth.shared.component.ComponentInitializationException;
import net.shibboleth.shared.resolver.CriteriaSet;
import net.shibboleth.shared.resolver.ResolverException;
import org.opensaml.core.xml.io.MarshallingException;
import org.opensaml.core.xml.util.XMLObjectSupport;
import org.opensaml.saml.criterion.RoleDescriptorCriterion;
import org.opensaml.saml.saml2.metadata.EntityDescriptor;
import org.opensaml.saml.saml2.metadata.SSODescriptor;
import org.opensaml.saml.security.impl.MetadataCredentialResolver;
import org.opensaml.saml.security.impl.SAMLMetadataEncryptionParametersResolver;
import org.opensaml.security.credential.UsageType;
import org.opensaml.security.criteria.UsageCriterion;
import org.opensaml.xmlsec.EncryptionConfiguration;
import org.opensaml.xmlsec.config.impl.DefaultSecurityConfigurationBootstrap;
import org.opensaml.xmlsec.criterion.EncryptionConfigurationCriterion;
import org.opensaml.xmlsec.criterion.EncryptionOptionalCriterion;
import org.opensaml.xmlsec.encryption.EncryptedData;
import org.opensaml.xmlsec.encryption.support.DataEncryptionParameters;
import org.opensaml.xmlsec.encryption.support.Encrypter;
import org.opensaml.xmlsec.encryption.support.EncryptionException;
import org.opensaml.xmlsec.encryption.support.KeyEncryptionParameters;
import org.w3c.dom.Element;
import se.idsec.signservice.integration.config.IntegrationServiceConfiguration;
import se.idsec.signservice.integration.core.error.ErrorCode;
import se.idsec.signservice.integration.core.error.SignServiceIntegrationException;
import se.idsec.signservice.integration.core.error.impl.SignServiceProtocolException;
import se.idsec.signservice.integration.core.impl.CorrelationID;
import se.idsec.signservice.integration.security.IdpMetadataResolver;
import se.idsec.signservice.integration.security.MetadataException;
import se.idsec.signservice.integration.security.SignServiceEncryptException;
import se.idsec.signservice.integration.security.impl.EncryptionConfigurationWrapper;
import se.idsec.signservice.integration.security.impl.OpenSAMLEncryptionParameters;
import se.idsec.signservice.integration.signmessage.SignMessageParameters;
import se.idsec.signservice.integration.signmessage.SignMessageProcessor;
import se.idsec.signservice.utils.AssertThat;
import se.swedenconnect.opensaml.saml2.metadata.EntityDescriptorUtils;
import se.swedenconnect.opensaml.sweid.saml2.signservice.build.SignMessageBuilder;
import se.swedenconnect.opensaml.sweid.saml2.signservice.dss.EncryptedMessage;
import se.swedenconnect.opensaml.sweid.saml2.signservice.dss.SignMessageMimeTypeEnum;
import se.swedenconnect.schemas.csig.dssext_1_1.SignMessage;
import se.swedenconnect.xml.jaxb.JAXBContextUtils;

/**
 * SignMessageProcessor default implementation.
 *
 * @author Martin Lindström ([email protected])
 * @author Stefan Santesson ([email protected])
 */
@Slf4j
public class DefaultSignMessageProcessor implements SignMessageProcessor {

  /** The metadata resolver. */
  protected IdpMetadataResolver idpMetadataResolver;

  /** The resolver for encryption parameters. */
  protected SAMLMetadataEncryptionParametersResolver encryptionParametersResolver;

  /** The encrypter to use. */
  protected Encrypter encrypter = new Encrypter();

  /**
   * Constructor.
   */
  public DefaultSignMessageProcessor() {
    final MetadataCredentialResolver credentialResolver = new MetadataCredentialResolver();
    credentialResolver.setKeyInfoCredentialResolver(
        DefaultSecurityConfigurationBootstrap.buildBasicInlineKeyInfoCredentialResolver());
    try {
      credentialResolver.initialize();
    }
    catch (final ComponentInitializationException e) {
      // Can not happen
    }
    this.encryptionParametersResolver = new SAMLMetadataEncryptionParametersResolver(credentialResolver);
    this.encryptionParametersResolver.setMergeMetadataRSAOAEPParametersWithConfig(true);
    this.encryptionParametersResolver.setAutoGenerateDataEncryptionCredential(true);
  }

  /** {@inheritDoc} */
  @Override
  public SignMessage create(@Nonnull final SignMessageParameters input,
      @Nonnull final IntegrationServiceConfiguration config) throws SignServiceIntegrationException {

    final se.swedenconnect.opensaml.sweid.saml2.signservice.dss.SignMessage signMessage = SignMessageBuilder.builder()
        .message(input.getSignMessage())
        .mimeType(SignMessageMimeTypeEnum.parse(input.getMimeType()))
        .mustShow(input.getMustShow())
        .displayEntity(input.getDisplayEntity())
        .build();

    if (input.isPerformEncryption()) {
      log.debug("{}: Encrypting SignMessage for IdP '{}' ...", CorrelationID.id(), input.getDisplayEntity());

      // Find IdP metadata ...
      //
      final EntityDescriptor metadata = this.idpMetadataResolver.resolveMetadata(input.getDisplayEntity(), config);
      final SSODescriptor descriptor = EntityDescriptorUtils.getSSODescriptor(metadata);
      if (descriptor == null) {
        final String msg =
            String.format("Metadata for '%s' does not contain an SSO descriptor", input.getDisplayEntity());
        log.error("{}: {}", CorrelationID.id(), msg);
        throw new MetadataException(msg);
      }

      final EncryptionConfiguration econfig;
      if (config.getDefaultEncryptionParameters() instanceof OpenSAMLEncryptionParameters
          && ((OpenSAMLEncryptionParameters) config.getDefaultEncryptionParameters()).getSystemConfiguration() instanceof EncryptionConfiguration) {

        econfig = ((OpenSAMLEncryptionParameters) config.getDefaultEncryptionParameters())
            .getSystemConfiguration();
      }
      else {
        econfig = new EncryptionConfigurationWrapper(config.getDefaultEncryptionParameters());
      }

      final CriteriaSet criteriaSet = new CriteriaSet();
      criteriaSet.add(new RoleDescriptorCriterion(descriptor));
      criteriaSet.add(new UsageCriterion(UsageType.ENCRYPTION));
      criteriaSet.add(new EncryptionConfigurationCriterion(econfig));
      criteriaSet.add(new EncryptionOptionalCriterion(false));

      try {
        final org.opensaml.xmlsec.EncryptionParameters openSAMLEncryptionParameters =
            this.encryptionParametersResolver.resolveSingle(criteriaSet);

        // Let's encrypt!
        //
        final DataEncryptionParameters dataEncryptionParameters =
            new DataEncryptionParameters(openSAMLEncryptionParameters);
        final KeyEncryptionParameters kekParams =
            new KeyEncryptionParameters(openSAMLEncryptionParameters, input.getDisplayEntity());

        final EncryptedData encryptedData =
            this.encrypter.encryptElement(signMessage.getMessage(), dataEncryptionParameters, kekParams);

        final EncryptedMessage encryptedMessage =
            (EncryptedMessage) XMLObjectSupport.buildXMLObject(EncryptedMessage.DEFAULT_ELEMENT_NAME);
        encryptedMessage.setEncryptedData(encryptedData);

        signMessage.setMessage(null);
        signMessage.setEncryptedMessage(encryptedMessage);
      }
      catch (final ResolverException e) {
        log.error("{}: Error during resolve of encryption parameters", CorrelationID.id(), e);
        throw new SignServiceEncryptException(new ErrorCode.Code("resolve"), "Failed to resolve encryption parameters",
            e);
      }
      catch (final EncryptionException e) {
        final String msg = String.format("Encryption failure - %s", e.getMessage());
        log.error("{}: {}", CorrelationID.id(), msg);
        throw new SignServiceEncryptException(new ErrorCode.Code("crypt"), msg, e);
      }
      log.debug("{}: SignMessage successfully encrypted for IdP '{}'", CorrelationID.id(), input.getDisplayEntity());
    }

    try {
      final Element element = XMLObjectSupport.marshall(signMessage);
      final JAXBContext context = JAXBContextUtils.createJAXBContext(SignMessage.class);
      final Object jaxb = context.createUnmarshaller().unmarshal(element);
      return (SignMessage) jaxb;
    }
    catch (final MarshallingException | JAXBException e) {
      throw new SignServiceProtocolException("Failed to marshall DSS protocol object", e);
    }
  }

  /**
   * Assigns the metadata resolver for IdP metadata
   *
   * @param idpMetadataResolver metadata resolver
   */
  public void setIdpMetadataResolver(final IdpMetadataResolver idpMetadataResolver) {
    this.idpMetadataResolver = idpMetadataResolver;
  }

  /**
   * The encrypter to use.
   * 

* If not assigned, an instance of {@link org.opensaml.xmlsec.encryption.support.Encrypter} is used. *

* * @param encrypter the encrypter */ public void setEncrypter(final Encrypter encrypter) { if (encrypter != null) { this.encrypter = encrypter; } } /** * Ensures that all required properties have been assigned. * *

* Note: If executing in a Spring Framework environment this method is automatically invoked after all properties have * been assigned. Otherwise it should be explicitly invoked. *

* * @throws Exception if not all settings are correct */ @PostConstruct public void afterPropertiesSet() throws Exception { AssertThat.isNotNull(this.idpMetadataResolver, "'idpMetadataResolver' must be assigned"); } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy