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

org.digidoc4j.impl.asic.manifest.ManifestValidator Maven / Gradle / Ivy

Go to download

DigiDoc4j is a Java library for digitally signing documents and creating digital signature containers of signed documents

The newest version!
/* DigiDoc4J library
 *
 * This software is released under either the GNU Library General Public
 * License (see LICENSE.LGPL).
 *
 * Note that the only valid version of the LGPL license as far as this
 * project is concerned is the original GNU Library General Public License
 * Version 2.1, February 1999
 */

package org.digidoc4j.impl.asic.manifest;

import eu.europa.esig.dss.model.DSSDocument;
import eu.europa.esig.dss.xml.utils.DomUtils;
import org.apache.xml.security.signature.Reference;
import org.digidoc4j.Signature;
import org.digidoc4j.exceptions.DigiDoc4JException;
import org.digidoc4j.impl.asic.AsicSignature;
import org.digidoc4j.impl.asic.xades.XadesSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Node;

import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * For validating meta data within the manifest file and signature files.
 */
public class ManifestValidator {
  public static final String MANIFEST_PATH = "META-INF/manifest.xml";
  public static final String MIMETYPE_PATH = "mimetype";
  private static final Logger logger = LoggerFactory.getLogger(ManifestValidator.class);
  private final List detachedContents;
  private final ManifestParser manifestParser;
  private final Collection signatures;

  public ManifestValidator(ManifestParser manifestParser, List detachedContents,
                           Collection signatures) {
    this.manifestParser = manifestParser;
    this.detachedContents = detachedContents;
    this.signatures = signatures;
  }

  public static List validateEntries(Map manifestEntries,
                                                           Set signatureEntries,
                                                           String signatureId) {
    ArrayList errorMessages = new ArrayList<>();

    if (signatureEntries.size() == 0)
      return errorMessages;

    Set one = new HashSet<>(manifestEntries.values());
    Set onePrim = new HashSet<>(manifestEntries.values());
    Set two = new HashSet<>(signatureEntries);
    Set twoPrim = new HashSet<>();
    for (ManifestEntry manifestEntry : signatureEntries) {
      String mimeType = manifestEntry.getMimeType();
      String alterName = manifestEntry.getFileName().replaceAll("\\+", " ");
      twoPrim.add(new ManifestEntry(alterName, mimeType));
    }

    one.removeAll(signatureEntries);
    onePrim.removeAll(twoPrim);
    two.removeAll(manifestEntries.values());
    twoPrim.removeAll(manifestEntries.values());

    if (one.size() > 0 && onePrim.size() > 0) {
      for (ManifestEntry manifestEntry : one) {

        String fileName = manifestEntry.getFileName();
        ManifestEntry signatureEntry = signatureEntryForFile(fileName, signatureEntries);
        if (signatureEntry != null) {
          errorMessages.add(new ManifestErrorMessage("Manifest file has an entry for file <"
                  + fileName + "> with mimetype <"
                  + manifestEntry.getMimeType() + "> but the signature file for signature " + signatureId
                  + " indicates the mimetype is <" + signatureEntry.getMimeType() + ">", signatureId));
          two.remove(signatureEntry);
        } else {
          errorMessages.add(new ManifestErrorMessage("Manifest file has an entry for file <"
                  + fileName + "> with mimetype <"
                  + manifestEntry.getMimeType() + "> but the signature file for signature " + signatureId
                  + " does not have an entry for this file", signatureId));
        }
      }
    }

    if (two.size() > 0 && twoPrim.size() > 0) {
      for (ManifestEntry manifestEntry : two) {
        errorMessages.add(new ManifestErrorMessage("The signature file for signature "
                + signatureId + " has an entry for file <"
                + manifestEntry.getFileName() + "> with mimetype <" + manifestEntry.getMimeType()
                + "> but the manifest file does not have an entry for this file", signatureId));
      }
    }

    return errorMessages;
  }

  private static ManifestEntry signatureEntryForFile(String fileName, Set signatureEntries) {
    logger.debug("File name: " + fileName);
    for (ManifestEntry signatureEntry : signatureEntries) {
      if (fileName.equals(signatureEntry.getFileName())) {
        return signatureEntry;
      }
    }
    return null;
  }

  /**
   * Validate the container.
   *
   * @return list of error messages
   */
  public List validateDocument() {
    if (!manifestParser.containsManifestFile()) {
      String errorMessage = "Container does not contain manifest file.";
      logger.error(errorMessage);
      throw new DigiDoc4JException(errorMessage);
    }
    List errorMessages = new ArrayList<>();
    Map manifestEntries = manifestParser.getManifestFileItems();
    Set signatureEntries = new HashSet<>();

    for (Signature signature : signatures) {
      signatureEntries = getSignatureEntries(signature);

      errorMessages.addAll(validateEntries(manifestEntries, signatureEntries, signature.getId()));
    }

    errorMessages.addAll(validateFilesInContainer(signatureEntries));

    logger.info("Validation of meta data within the manifest file and signature files error count: "
            + errorMessages.size());
    return errorMessages;
  }

  private List validateFilesInContainer(Set signatureEntries) {
    List errorMessages = new ArrayList<>();
    if (signatureEntries.size() == 0) {
      return errorMessages;
    }
    Set signatureEntriesFileNames = this.getFileNamesFromManifestEntrySet(signatureEntries);
    List filesInContainer = getFilesInContainer();
    for (String fileInContainer : filesInContainer) {
      String alterName = fileInContainer.replaceAll("\\ ", "+");
      if (!signatureEntriesFileNames.contains(fileInContainer) && !signatureEntriesFileNames.contains(alterName)) {
        errorMessages.add(new ManifestErrorMessage(String.format("Container contains a file named <%s> which is not "
                + "found in the signature file", fileInContainer)));
      }
    }
    return errorMessages;
  }

  private Set getFileNamesFromManifestEntrySet(Set signatureEntries) {
    Set signatureEntriesFileNames = new HashSet<>(signatureEntries.size());


    for (ManifestEntry entry : signatureEntries) {
      signatureEntriesFileNames.add(entry.getFileName());
    }
    return signatureEntriesFileNames;
  }

  private Set getSignatureEntries(Signature signature) {
    Set signatureEntries = new HashSet<>();
    XadesSignature origin = ((AsicSignature) signature).getOrigin();
    List references = origin.getReferences();
    for (Reference reference : references) {
      if (reference.getType().equals("")) {
        String mimeTypeString = null;

        Node signatureNode = origin.getDssSignature().getSignatureElement();
        Node node = DomUtils.getNode(signatureNode, "./ds:SignedInfo/ds:Reference[@URI=\""
                + reference.getURI() + "\"]");
        if (node != null) {
          String referenceId = node.getAttributes().getNamedItem("Id").getNodeValue();
          String xAdESPrefix = origin.getDssSignature().getXAdESPaths().getNamespace().getPrefix();
          mimeTypeString = DomUtils.getValue(signatureNode,
                  "./ds:Object/" + xAdESPrefix + ":QualifyingProperties/" + xAdESPrefix + ":SignedProperties/"
                          + xAdESPrefix + ":SignedDataObjectProperties/" + xAdESPrefix + ":DataObjectFormat"
                          + "[@ObjectReference=\"#" + referenceId + "\"]/" + xAdESPrefix + ":MimeType");
        }

        // TODO: mimeTypeString == null ? node == null?
        String uri = getFileURI(reference);
        signatureEntries.add(new ManifestEntry(uri, mimeTypeString));
      }
    }

    return signatureEntries;
  }

  private String getFileURI(Reference reference) {
    String uri = reference.getURI();
    try {
      uri = new URI(uri).getPath();
    } catch (URISyntaxException e) {
      logger.warn("Does not parse as an URI, therefore assuming it's not encoded: '" + uri + "'");
    }

    return uri;
  }

  private List getFilesInContainer() {
    List fileEntries = new ArrayList<>();
    for (DSSDocument detachedContent : detachedContents) {
      String name = detachedContent.getName();
      fileEntries.add(name);
    }
    return fileEntries;
  }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy