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

eu.europa.esig.dss.xades.signature.EnvelopedSignatureBuilder Maven / Gradle / Ivy

/**
 * DSS - Digital Signature Services
 * Copyright (C) 2015 European Commission, provided under the CEF programme
 *
 * This file is part of the "DSS - Digital Signature Services" project.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 */
package eu.europa.esig.dss.xades.signature;

import java.util.ArrayList;
import java.util.List;

import javax.xml.crypto.dsig.CanonicalizationMethod;
import javax.xml.crypto.dsig.XMLSignature;

import org.apache.xml.security.transforms.Transforms;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import eu.europa.esig.dss.DSSDocument;
import eu.europa.esig.dss.DSSException;
import eu.europa.esig.dss.DomUtils;
import eu.europa.esig.dss.InMemoryDocument;
import eu.europa.esig.dss.utils.Utils;
import eu.europa.esig.dss.validation.CertificateVerifier;
import eu.europa.esig.dss.xades.DSSReference;
import eu.europa.esig.dss.xades.DSSTransform;
import eu.europa.esig.dss.xades.DSSXMLUtils;
import eu.europa.esig.dss.xades.XAdESSignatureParameters;
import eu.europa.esig.dss.xades.XPathQueryHolder;

/**
 * This class handles the specifics of the enveloped XML signature
 *
 */
class EnvelopedSignatureBuilder extends XAdESSignatureBuilder {

	/**
	 * The default constructor for EnvelopedSignatureBuilder. The enveloped signature uses by default the exclusive
	 * method of canonicalization.
	 * 
	 * @param params
	 *            The set of parameters relating to the structure and process of the creation or extension of the
	 *            electronic signature.
	 * @param origDoc
	 *            The original document to sign.
	 * @param certificateVerifier
	 */
	public EnvelopedSignatureBuilder(final XAdESSignatureParameters params, final DSSDocument origDoc, final CertificateVerifier certificateVerifier) {
		super(params, origDoc, certificateVerifier);
		setCanonicalizationMethods(params, CanonicalizationMethod.EXCLUSIVE);
	}

	/**
	 * In case of enveloped signature, the document should be the original file. Important for inclusive
	 * canonicalization and namespaces
	 */
	@Override
	protected Document buildRootDocumentDom() {
		return DomUtils.buildDOM(detachedDocument);
	}

	@Override
	protected Node getParentNodeOfSignature() {
		final String xPathLocationString = params.getXPathLocationString();
		if (Utils.isStringNotEmpty(xPathLocationString)) {
			return DomUtils.getElement(documentDom, xPathLocationString);
		}
		return documentDom.getDocumentElement();
	}

	@Override
	protected DSSReference createReference(DSSDocument document, int referenceIndex) {

		DSSReference dssReference = new DSSReference();
		dssReference.setId("r-id-" + referenceIndex);
		dssReference.setUri("");
		dssReference.setContents(document);
		dssReference.setDigestMethodAlgorithm(params.getDigestAlgorithm());

		final List dssTransformList = new ArrayList();

		// For parallel signatures
		DSSTransform dssTransform = new DSSTransform();
		dssTransform.setAlgorithm(Transforms.TRANSFORM_XPATH);
		dssTransform.setElementName(DS_XPATH);
		dssTransform.setNamespace(XMLSignature.XMLNS);
		dssTransform.setTextContent(NOT_ANCESTOR_OR_SELF_DS_SIGNATURE);
		dssTransformList.add(dssTransform);

		// Canonicalization is the last operation, its better to operate the canonicalization on the smaller document
		dssTransform = new DSSTransform();
		dssTransform.setAlgorithm(CanonicalizationMethod.EXCLUSIVE);
		dssTransformList.add(dssTransform);

		dssReference.setTransforms(dssTransformList);

		return dssReference;
	}

	/**
	 * Preconditions:
	 * - The reference data is XML
	 * - The last transformation is canonicalization.
	 *
	 * @param reference
	 *            {@code DSSReference} to be transformed
	 * @return {@code DSSDocument} containing transformed reference's data
	 */
	@Override
	protected DSSDocument transformReference(final DSSReference reference) {

		DSSDocument dssDocument = reference.getContents();
		final List transforms = reference.getTransforms();
		if (Utils.isCollectionEmpty(transforms)) {
			return dssDocument;
		}

		// In the case of ENVELOPED signature the document to sign is an XML. However one of the references can point to
		// another document this test case is not taken into account!

		Node nodeToTransform = null;
		final String uri = reference.getUri();
		// Check if the reference is related to the whole document
		if (Utils.isStringNotBlank(uri) && uri.startsWith("#") && !isXPointer(uri)) {

			final Document document = DomUtils.buildDOM(dssDocument);
			DSSXMLUtils.recursiveIdBrowse(document.getDocumentElement());
			final String uri_id = uri.substring(1);
			nodeToTransform = document.getElementById(uri_id);
		}
		byte[] transformedReferenceBytes = applyTransformations(dssDocument, transforms, nodeToTransform);
		return new InMemoryDocument(transformedReferenceBytes);
	}

	private byte[] applyTransformations(DSSDocument dssDocument, final List transforms, Node nodeToTransform) {
		byte[] transformedReferenceBytes = null;
		for (final DSSTransform transform : transforms) {

			final String transformAlgorithm = transform.getAlgorithm();
			if (Transforms.TRANSFORM_XPATH.equals(transformAlgorithm)) {

				final DSSTransformXPath transformXPath = new DSSTransformXPath(transform);
				// At the moment it is impossible to go through a medium other than byte array (Set, octet stream,
				// Node). Further investigation is needed.
				final byte[] transformedBytes = nodeToTransform == null ? transformXPath.transform(dssDocument) : transformXPath.transform(nodeToTransform);
				dssDocument = new InMemoryDocument(transformedBytes);
				nodeToTransform = DomUtils.buildDOM(dssDocument);
			} else if (DSSXMLUtils.canCanonicalize(transformAlgorithm)) {

				if (nodeToTransform == null) {
					nodeToTransform = DomUtils.buildDOM(dssDocument);
				}
				transformedReferenceBytes = DSSXMLUtils.canonicalizeSubtree(transformAlgorithm, nodeToTransform);
				// The supposition is made that the last transformation is the canonicalization
				break;
			} else if (CanonicalizationMethod.ENVELOPED.equals(transformAlgorithm)) {

				// do nothing the new signature is not existing yet!
				// removeExistingSignatures(document);
			} else {
				throw new DSSException("The transformation is not implemented yet, please transform the reference before signing!");
			}
		}
		return transformedReferenceBytes;
	}

	private static boolean isXPointer(final String uri) {
		final boolean xPointer = uri.startsWith("#xpointer(") || uri.startsWith("#xmlns(");
		return xPointer;
	}

	/**
	 * Bob --> This method is not used anymore, but it can replace {@code NOT_ANCESTOR_OR_SELF_DS_SIGNATURE}
	 * transformation. Performance test should be performed!
	 * In case of the enveloped signature the existing signatures are removed.
	 *
	 * @param domDoc
	 *            {@code Document} containing the signatures to analyse
	 */
	protected void removeExistingSignatures(final Document domDoc) {

		final NodeList signatureNodeList = domDoc.getElementsByTagNameNS(XMLSignature.XMLNS, XPathQueryHolder.XMLE_SIGNATURE);
		for (int ii = signatureNodeList.getLength() - 1; ii >= 0; ii--) {
			final Element signatureDOM = (Element) signatureNodeList.item(ii);
			signatureDOM.getParentNode().removeChild(signatureDOM);
		}
	}

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy