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

se.idsec.sigval.xml.verify.impl.XMLSignedDocumentValidator Maven / Gradle / Ivy

/*
 * Copyright (c) 2020. 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.sigval.xml.verify.impl;

import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import se.idsec.signservice.security.certificate.CertificateValidator;
import se.idsec.signservice.security.sign.SignatureValidationResult;
import se.idsec.signservice.security.sign.xml.XMLSignatureLocation;
import se.idsec.sigval.commons.data.SignedDocumentValidationResult;
import se.idsec.sigval.xml.data.ExtendedXmlSigvalResult;
import se.idsec.sigval.xml.utils.XMLSigUtils;
import se.idsec.sigval.xml.verify.ExtendedXMLSignedDocumentValidator;
import se.idsec.sigval.xml.verify.XMLSignatureElementValidator;
import se.idsec.sigval.xml.xmlstruct.SignatureData;
import se.idsec.sigval.xml.xmlstruct.XMLSignatureContext;
import se.idsec.sigval.xml.xmlstruct.XMLSignatureContextFactory;
import se.idsec.sigval.xml.xmlstruct.impl.DefaultXMLSignatureContextFactory;

import java.security.SignatureException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;

/**
 * XML Document signature validator for validating signatures on XML documents
 *
 * @author Martin Lindström ([email protected])
 * @author Stefan Santesson ([email protected])
 */
@Slf4j
public class XMLSignedDocumentValidator implements ExtendedXMLSignedDocumentValidator {

  /** Validator for individual signatures */
  private final XMLSignatureElementValidator signatureElementValidator;

  /**
   * Factory for getting an implementation of the signature context provider providing info about the signed document
   *
   * @param signatureContextFactory signature context factory
   */
  @Setter private XMLSignatureContextFactory signatureContextFactory;

  /**
   * Constructor setting up the validator
   *
   * @param signatureElementValidator signature element validator
   */
  public XMLSignedDocumentValidator(XMLSignatureElementValidator signatureElementValidator) {
    this.signatureElementValidator = signatureElementValidator;
    this.signatureContextFactory = new DefaultXMLSignatureContextFactory();
  }

  /** {@inheritDoc} */
  @Override public SignedDocumentValidationResult extendedResultValidation(Document document)
    throws SignatureException {
    return getConcludingSigVerifyResult(validate(document));
  }

  /** {@inheritDoc} */
  @Override public List validate(Document document) throws SignatureException {
    // First locate all signature elements ...
    //
    List signatures = XMLSigUtils.getSignatures(document);
    try {
      return this.validate(document, signatures);
    }
    catch (Exception e) {
      log.error("Error validating XML signatures: {}", e.getMessage());
      throw new SignatureException(e.getMessage(), e);
    }
  }

  /** {@inheritDoc} */
  @Override public List validate(Document document, XMLSignatureLocation signatureLocation)
    throws SignatureException {
    if (signatureLocation == null) {
      return this.validate(document);
    }
    try {
      final Element signature = signatureLocation.getSignature(document);
      if (signature == null) {
        // We return exception in this case since a specific signature location was requested, but not found.
        throw new SignatureException("Could not find Signature element");
      }
      return this.validate(document, Collections.singletonList(signature));
    }
    catch (Exception e) {
      log.error("Error validating XML signatures: {}", e.getMessage());
      throw new SignatureException(e.getMessage(), e);
    }
  }

  /**
   * Validates the supplied signatures.
   *
   * @param document   the document containing the signatures
   * @param signatures the signatures
   * @return a list of result objects
   * @throws Exception on errors validating signature
   */
  protected List validate(final Document document, final List signatures) throws Exception {

    try {
      XMLSignatureContext signatureContext = signatureContextFactory.getSignatureContext(document);

      // Verify all signatures ...
      List results = new ArrayList<>();
      for (Element signature : signatures) {
        SignatureData signatureData = signatureContext.getSignatureData(signature);
        results.add(signatureElementValidator.validateSignature(signature, signatureData));
      }
      return results;
    }
    catch (Exception e) {
      throw e;
    }
  }

  /** {@inheritDoc} */
  @Override
  public boolean isSigned(final Document document) throws IllegalArgumentException {
    try {
      NodeList signatureElements = document.getElementsByTagNameNS(javax.xml.crypto.dsig.XMLSignature.XMLNS, "Signature");
      return signatureElements.getLength() > 0;
    }
    catch (Exception e) {
      throw new IllegalArgumentException("Invalid document", e);
    }
  }

  /** {@inheritDoc} */
  @Override
  public List getRequiredSignerCertificates() {
    return new ArrayList<>();
  }

  /** {@inheritDoc} */
  @Override
  public CertificateValidator getCertificateValidator() {
    return this.signatureElementValidator.getCertificateValidator();
  }

  /**
   * Compile a complete XML signature verification result object from the list of individual signature results
   *
   * @param sigVerifyResultList list of individual signature validation results. Each result must be of type {@link ExtendedXmlSigvalResult}
   * @return PDF signature validation result objects
   */
  public static SignedDocumentValidationResult getConcludingSigVerifyResult(
    List sigVerifyResultList) {
    SignedDocumentValidationResult sigVerifyResult = new SignedDocumentValidationResult<>();
    List extendedPdfSigValResults = new ArrayList<>();
    try {
      extendedPdfSigValResults = sigVerifyResultList.stream()
        .map(signatureValidationResult -> (ExtendedXmlSigvalResult) signatureValidationResult)
        .collect(Collectors.toList());
      sigVerifyResult.setSignatureValidationResults(extendedPdfSigValResults);
    }
    catch (Exception ex) {
      throw new IllegalArgumentException("Provided results are not instances of ExtendedXmlSigvalResult");
    }
    // Test if there are no signatures
    if (sigVerifyResultList.isEmpty()) {
      sigVerifyResult.setSignatureCount(0);
      sigVerifyResult.setStatusMessage("No signatures");
      sigVerifyResult.setValidSignatureCount(0);
      sigVerifyResult.setCompleteSuccess(false);
      sigVerifyResult.setSigned(false);
      return sigVerifyResult;
    }

    //Get valid signatures
    sigVerifyResult.setSigned(true);
    sigVerifyResult.setSignatureCount(sigVerifyResultList.size());
    List validSignatureResultList = extendedPdfSigValResults.stream()
      .filter(cmsSigVerifyResult -> cmsSigVerifyResult.isSuccess())
      .collect(Collectors.toList());

    sigVerifyResult.setValidSignatureCount(validSignatureResultList.size());
    if (validSignatureResultList.isEmpty()) {
      //No valid signatures
      sigVerifyResult.setCompleteSuccess(false);
      sigVerifyResult.setStatusMessage("No valid signatures");
      return sigVerifyResult;
    }

    //Reaching this point means that there are valid signatures.
    if (sigVerifyResult.getSignatureCount() == validSignatureResultList.size()) {
      sigVerifyResult.setStatusMessage("OK");
      sigVerifyResult.setCompleteSuccess(true);
    }
    else {
      sigVerifyResult.setStatusMessage("Some signatures are valid and some are invalid");
      sigVerifyResult.setCompleteSuccess(false);
    }

    //Check if any valid signature signs the whole document
    boolean validSigSignsWholeDoc = validSignatureResultList.stream()
      .filter(signatureValidationResult -> signatureValidationResult.isCoversDocument())
      .findFirst().isPresent();

    sigVerifyResult.setValidSignatureSignsWholeDocument(validSigSignsWholeDoc);

    return sigVerifyResult;
  }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy