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

eu.europa.esig.dss.xades.validation.XAdESSignature 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.validation;

import eu.europa.esig.dss.enumerations.DigestAlgorithm;
import eu.europa.esig.dss.enumerations.DigestMatcherType;
import eu.europa.esig.dss.enumerations.EncryptionAlgorithm;
import eu.europa.esig.dss.enumerations.EndorsementType;
import eu.europa.esig.dss.enumerations.MaskGenerationFunction;
import eu.europa.esig.dss.enumerations.ObjectIdentifierQualifier;
import eu.europa.esig.dss.enumerations.SignatureAlgorithm;
import eu.europa.esig.dss.enumerations.SignatureForm;
import eu.europa.esig.dss.enumerations.SignatureLevel;
import eu.europa.esig.dss.model.DSSDocument;
import eu.europa.esig.dss.model.DSSException;
import eu.europa.esig.dss.model.Digest;
import eu.europa.esig.dss.model.InMemoryDocument;
import eu.europa.esig.dss.model.ReferenceValidation;
import eu.europa.esig.dss.model.SignaturePolicyStore;
import eu.europa.esig.dss.model.SpDocSpecification;
import eu.europa.esig.dss.model.UserNotice;
import eu.europa.esig.dss.model.scope.SignatureScope;
import eu.europa.esig.dss.spi.DSSSecurityProvider;
import eu.europa.esig.dss.spi.DSSUtils;
import eu.europa.esig.dss.spi.SignatureCertificateSource;
import eu.europa.esig.dss.spi.x509.CandidatesForSigningCertificate;
import eu.europa.esig.dss.spi.x509.CertificateValidity;
import eu.europa.esig.dss.spi.x509.SignatureIntegrityValidator;
import eu.europa.esig.dss.spi.x509.revocation.crl.OfflineCRLSource;
import eu.europa.esig.dss.spi.x509.revocation.ocsp.OfflineOCSPSource;
import eu.europa.esig.dss.spi.x509.tsp.TimestampToken;
import eu.europa.esig.dss.utils.Utils;
import eu.europa.esig.dss.spi.signature.AdvancedSignature;
import eu.europa.esig.dss.spi.validation.CertificateVerifier;
import eu.europa.esig.dss.model.signature.CommitmentTypeIndication;
import eu.europa.esig.dss.spi.signature.DefaultAdvancedSignature;
import eu.europa.esig.dss.model.signature.SignatureCryptographicVerification;
import eu.europa.esig.dss.model.signature.SignatureDigestReference;
import eu.europa.esig.dss.spi.signature.identifier.SignatureIdentifierBuilder;
import eu.europa.esig.dss.model.signature.SignatureProductionPlace;
import eu.europa.esig.dss.model.signature.SignerRole;
import eu.europa.esig.dss.xades.DSSXMLUtils;
import eu.europa.esig.dss.xades.EnforcedResolverFragment;
import eu.europa.esig.dss.xades.reference.XAdESReferenceValidation;
import eu.europa.esig.dss.xades.validation.scope.XAdESSignatureScopeFinder;
import eu.europa.esig.dss.xades.validation.timestamp.XAdESTimestampSource;
import eu.europa.esig.dss.xml.common.definition.DSSNamespace;
import eu.europa.esig.dss.xml.utils.DomUtils;
import eu.europa.esig.dss.xml.utils.SantuarioInitializer;
import eu.europa.esig.dss.xml.utils.XMLCanonicalizer;
import eu.europa.esig.dss.xades.definition.XAdESNamespace;
import eu.europa.esig.dss.xades.definition.XAdESPath;
import eu.europa.esig.dss.xades.definition.xades132.XAdES132Attribute;
import eu.europa.esig.dss.xades.definition.xades132.XAdES132Element;
import eu.europa.esig.dss.xades.definition.xades132.XAdES132Path;
import eu.europa.esig.dss.xades.definition.xades141.XAdES141Element;
import eu.europa.esig.dss.xml.common.definition.xmldsig.XMLDSigAttribute;
import eu.europa.esig.dss.xml.common.definition.xmldsig.XMLDSigElement;
import eu.europa.esig.dss.xml.common.definition.xmldsig.XMLDSigPath;
import org.apache.xml.security.algorithms.JCEMapper;
import org.apache.xml.security.exceptions.XMLSecurityException;
import org.apache.xml.security.signature.Reference;
import org.apache.xml.security.signature.SignedInfo;
import org.apache.xml.security.signature.XMLSignature;
import org.apache.xml.security.utils.resolver.ResourceResolver;
import org.apache.xml.security.utils.resolver.implementations.ResolverXPointer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import javax.xml.crypto.dsig.CanonicalizationMethod;
import javax.xml.transform.dom.DOMSource;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Objects;

/**
 * Parse an XAdES signature structure. Note that for each signature to be validated a new instance of this object must
 * be created.
 *
 */
public class XAdESSignature extends DefaultAdvancedSignature {
	
	private static final long serialVersionUID = -2639858392612722185L;

	private static final Logger LOG = LoggerFactory.getLogger(XAdESSignature.class);
	
	/**
	 * The default canonicalization method used in {@link SignatureDigestReference} computation
	 */
	private static final String DEFAULT_CANONICALIZATION_METHOD = CanonicalizationMethod.EXCLUSIVE;

	/**
	 * This variable contains the list of {@code XAdESPaths} adapted to the specific
	 * signature schema.
	 */
	private final List xadesPathHolders;

	/** The current signature element */
	private final Element signatureElement;

	/** The XMLDSIG namespace */
	private DSSNamespace xmldSigNamespace;

	/** The current signature xades namespace */
	private DSSNamespace xadesNamespace;

	/** The XAdES XPath to use */
	private XAdESPath xadesPath;

	/** Defines if the XSW protection shall be disabled (false by default) */
	private boolean disableXSWProtection = false;

	/** Cached Apache Santuario Signature */
	private transient XMLSignature santuarioSignature;
	
	/**
	 * A signature identifier provided by a Driving Application.
	 */
	private String daIdentifier;

	/**
	 * This variable contains all references found within the signature. They are extracted when the method
	 * {@code checkSignatureIntegrity} is called.
	 */
	private transient List references;

	static {

		SantuarioInitializer.init();

		DSSXMLUtils.registerXAdESNamespaces();

		//
		// Set the default JCE algorithms
		//
		JCEMapper.setProviderId(DSSSecurityProvider.getSecurityProviderName());
		JCEMapper.registerDefaultAlgorithms();

		/**
		 * Adds the support of not standard algorithm name: http://www.w3.org/2001/04/xmldsig-more/rsa-ripemd160. Used
		 * by some AT signature providers. The BC
		 * provider must be previously added.
		 */
		final JCEMapper.Algorithm notStandardAlgorithm = new JCEMapper.Algorithm("", SignatureAlgorithm.RSA_RIPEMD160.getJCEId(), "Signature");
		JCEMapper.register(SignatureRSARIPEMD160AT.XML_ID, notStandardAlgorithm);
		try {
			org.apache.xml.security.algorithms.SignatureAlgorithm.register(SignatureRSARIPEMD160AT.XML_ID, SignatureRSARIPEMD160AT.class);
		} catch (Exception e) {
			LOG.error("ECDSA_RIPEMD160AT algorithm initialisation failed.", e);
		}

		initDefaultResolvers();

	}

	/**
	 * Customized
	 * org.apache.xml.security.utils.resolver.ResourceResolver.registerDefaultResolvers()
	 *
	 * Ignore references which point to a file (file://) or external http urls
	 * Enforce ResolverFragment against XPath injections
	 */
	private static void initDefaultResolvers() {
		ResourceResolver.register(new EnforcedResolverFragment(), false);
		ResourceResolver.register(new ResolverXPointer(), false);
	}

	/**
	 * This constructor is used when creating the signature. The default {@code XPathQueryHolder} is set.
	 *
	 * @param signatureElement
	 *            the signature DOM element
	 */
	public XAdESSignature(final Element signatureElement) {
		this(signatureElement, Arrays.asList(new XAdES132Path()));
	}

	/**
	 * The default constructor for XAdESSignature.
	 *
	 * @param signatureElement
	 *                          the signature DOM element
	 * @param xadesPathHolders
	 *                          List of {@code XAdESPaths} to use when handling
	 *                          signature
	 */
	public XAdESSignature(final Element signatureElement, final List xadesPathHolders) {
		Objects.requireNonNull(signatureElement, "Signature Element cannot be null");
		this.signatureElement = signatureElement;
		this.xadesPathHolders = xadesPathHolders;
		initialiseSettings();
	}

	/**
	 * NOT RECOMMENDED : This parameter allows to disable protection against XML
	 * Signature wrapping attacks (XSW). It disables the research by XPath
	 * expression for defined Type attributes.
	 * 
	 * @param disableXSWProtection
	 *                             true to disable the protection
	 */
	public void setDisableXSWProtection(boolean disableXSWProtection) {
		this.disableXSWProtection = disableXSWProtection;
	}

	/**
	 * This method is called when creating a new instance of the {@code XAdESSignature} with unknown schema.
	 */
	private void initialiseSettings() {
		recursiveNamespaceBrowser(signatureElement);

		if (xadesPath == null) {
			LOG.warn("There is no suitable XAdESPaths / XAdESNamespace to manage the signature. The default ones will be used.");
			xadesPath = new XAdES132Path();
			xadesNamespace = XAdESNamespace.XADES_132;
		}
	}

	/**
	 * This method sets the namespace which will determinate the {@code XAdESPaths} to use. The content of the
	 * Transform element is ignored.
	 *
	 * @param element {@link Element}
	 */
	public void recursiveNamespaceBrowser(final Element element) {
		for (int ii = 0; ii < element.getChildNodes().getLength(); ii++) {
			final Node node = element.getChildNodes().item(ii);
			if (node.getNodeType() == Node.ELEMENT_NODE) {
				final String prefix = node.getPrefix();
				final Element childElement = (Element) node;
				final String namespaceURI = childElement.getNamespaceURI();
				final String localName = childElement.getLocalName();
				if (XMLDSigElement.TRANSFORM.isSameTagName(localName) && XMLDSigElement.TRANSFORM.getURI().equals(namespaceURI)) {
					xmldSigNamespace = new DSSNamespace(namespaceURI, prefix);
					continue;
				} else if (XAdES132Element.QUALIFYING_PROPERTIES.isSameTagName(localName)) {
					setXAdESPathAndNamespace(prefix, namespaceURI);
					return;
				}
				recursiveNamespaceBrowser(childElement);
			}
		}
	}

	private void setXAdESPathAndNamespace(final String prefix, final String namespaceURI) {
		for (final XAdESPath currentXAdESPaths : xadesPathHolders) {
			if (currentXAdESPaths.getNamespace().isSameUri(namespaceURI)) {
				this.xadesPath = currentXAdESPaths;
				this.xadesNamespace = new DSSNamespace(namespaceURI, prefix);
			}
		}
	}

	/**
	 * Returns a list of used {@code XAdESPaths}
	 *
	 * @return a list of {@code XAdESPaths}
	 */
	public List getXAdESPathsHolders() {
		return xadesPathHolders;
	}

	/**
	 * Gets the current {@code XAdESPaths}
	 *
	 * @return {@link XAdESPath}
	 */
	public XAdESPath getXAdESPaths() {
		return xadesPath;
	}

	/**
	 * Returns the XMLDSIG namespace
	 *
	 * @return {@link DSSNamespace}
	 */
	public DSSNamespace getXmldSigNamespace() {
		return xmldSigNamespace;
	}

	/**
	 * Returns the XAdES namespace
	 *
	 * @return {@link DSSNamespace}
	 */
	public DSSNamespace getXadesNamespace() {
		return xadesNamespace;
	}

	/**
	 * Returns the w3c.dom encapsulated signature element.
	 *
	 * @return the signatureElement
	 */
	public Element getSignatureElement() {
		return signatureElement;
	}

	@Override
	public SignatureForm getSignatureForm() {
		return SignatureForm.XAdES;
	}

	@Override
	public EncryptionAlgorithm getEncryptionAlgorithm() {
		final SignatureAlgorithm signatureAlgorithm = getSignatureAlgorithm();
		if (signatureAlgorithm == null) {
			return null;
		}
		return signatureAlgorithm.getEncryptionAlgorithm();
	}

	@Override
	public DigestAlgorithm getDigestAlgorithm() {
		final SignatureAlgorithm signatureAlgorithm = getSignatureAlgorithm();
		if (signatureAlgorithm == null) {
			return null;
		}
		return signatureAlgorithm.getDigestAlgorithm();
	}

	@Override
	@Deprecated
	public MaskGenerationFunction getMaskGenerationFunction() {
		EncryptionAlgorithm encryptionAlgorithm = getEncryptionAlgorithm();
		if (EncryptionAlgorithm.RSASSA_PSS == encryptionAlgorithm) {
			return MaskGenerationFunction.MGF1;
		}
		return null;
	}

	@Override
	public SignatureAlgorithm getSignatureAlgorithm() {
		final String xmlName = DomUtils.getElement(signatureElement, XMLDSigPath.SIGNATURE_METHOD_PATH)
				.getAttribute(XMLDSigAttribute.ALGORITHM.getAttributeName());
		SignatureAlgorithm signatureAlgorithm =  SignatureAlgorithm.forXML(xmlName, null);
		if (signatureAlgorithm == null) {
			LOG.warn("SignatureAlgorithm '{}' is not supported!", xmlName);
		}
		return signatureAlgorithm;
	}

	@Override
	public SignatureCertificateSource getCertificateSource() {
		if (offlineCertificateSource == null) {
			offlineCertificateSource = new XAdESCertificateSource(signatureElement, xadesPath);
		}
		return offlineCertificateSource;
	}

	@Override
	public OfflineCRLSource getCRLSource() {
		if (signatureCRLSource == null) {
			signatureCRLSource = new XAdESCRLSource(signatureElement, xadesPath);
		}
		return signatureCRLSource;
	}

	@Override
	public OfflineOCSPSource getOCSPSource() {
		if (signatureOCSPSource == null) {
			signatureOCSPSource = new XAdESOCSPSource(signatureElement, xadesPath);
		}
		return signatureOCSPSource;
	}
	
	@Override
	public XAdESTimestampSource getTimestampSource() {
		if (signatureTimestampSource == null) {
			signatureTimestampSource = new XAdESTimestampSource(this);
		}
		return (XAdESTimestampSource) signatureTimestampSource;
	}

	@Override
	public Date getSigningTime() {

		final Element signingTimeEl = DomUtils.getElement(signatureElement, xadesPath.getSigningTimePath());
		if (signingTimeEl == null) {
			return null;
		}
		final String text = signingTimeEl.getTextContent();
		return DomUtils.getDate(text);
	}

	@Override
	public XAdESSignaturePolicy getSignaturePolicy() {
		return (XAdESSignaturePolicy) super.getSignaturePolicy();
	}

	@Override
	protected XAdESSignaturePolicy buildSignaturePolicy() {
		XAdESSignaturePolicy xadesSignaturePolicy = null;

		final Element policyIdentifier = DomUtils.getElement(signatureElement, xadesPath.getSignaturePolicyIdentifierPath());
		if (policyIdentifier != null) {
			// There is a policy
			final Element policyId = DomUtils.getElement(policyIdentifier, xadesPath.getCurrentSignaturePolicyId());
			if (policyId != null) {
				// Explicit policy
				String policyUrlString = null;

				ObjectIdentifierQualifier qualifier = null;
				String qualifierString = policyId.getAttribute(XAdES132Attribute.QUALIFIER.getAttributeName());
				if (Utils.isStringNotBlank(qualifierString)) {
					qualifier = ObjectIdentifierQualifier.fromValue(qualifierString);
				}

				String policyIdString = policyId.getTextContent();
				policyIdString = DSSUtils.getObjectIdentifierValue(policyIdString, qualifier);
				if (Utils.isStringNotBlank(policyIdString) && !DSSUtils.isUrnOid(policyIdString) && !DSSUtils.isOidCode(policyIdString)) {
					policyUrlString = policyIdString;
				}

				xadesSignaturePolicy = new XAdESSignaturePolicy(policyIdString);

				final Digest digest = DSSXMLUtils.getDigestAndValue(DomUtils.getElement(policyIdentifier, xadesPath.getCurrentSignaturePolicyDigestAlgAndValue()));
				xadesSignaturePolicy.setDigest(digest);

				final Element policyUrl = DomUtils.getElement(policyIdentifier, xadesPath.getCurrentSignaturePolicySPURI());
				if (policyUrl != null) {
					policyUrlString = policyUrl.getTextContent();
					policyUrlString = DSSUtils.trimWhitespacesAndNewlines(policyUrlString);
				}
				xadesSignaturePolicy.setUri(policyUrlString);

				final Element spUserNotice = DomUtils.getElement(policyIdentifier, xadesPath.getCurrentSignaturePolicySPUserNotice());
				if (spUserNotice != null) {
					xadesSignaturePolicy.setUserNotice(buildSPUserNotice(spUserNotice));
				}

				String currentSignaturePolicySPDocSpecificationPath = xadesPath.getCurrentSignaturePolicySPDocSpecification();
				if (Utils.isStringNotEmpty(currentSignaturePolicySPDocSpecificationPath)) {
					final Element spDocSpecification = DomUtils.getElement(policyIdentifier, currentSignaturePolicySPDocSpecificationPath);
					if (spDocSpecification != null) {
						xadesSignaturePolicy.setDocSpecification(buildSpDocSpecification(spDocSpecification));
					}
				}

				final Element policyDescription = DomUtils.getElement(policyIdentifier, xadesPath.getCurrentSignaturePolicyDescription());
				if (policyDescription != null && Utils.isStringNotEmpty(policyDescription.getTextContent())) {
					xadesSignaturePolicy.setDescription(policyDescription.getTextContent());
				}

				final Element docRefsNode = DomUtils.getElement(policyIdentifier, xadesPath.getCurrentSignaturePolicyDocumentationReferences());
				if (docRefsNode != null) {
					xadesSignaturePolicy.setDocumentationReferences(getDocumentationReferences(docRefsNode));
				}

				final Element transformsNode = DomUtils.getElement(policyIdentifier, xadesPath.getCurrentSignaturePolicyTransforms());
				if (transformsNode != null) {
					xadesSignaturePolicy.setTransforms(transformsNode);
					xadesSignaturePolicy.setHashAsInTechnicalSpecification(isHashComputationAsInPolicySpecification(transformsNode));
				}

			} else {
				// Implicit policy
				final Element signaturePolicyImplied = DomUtils.getElement(policyIdentifier, xadesPath.getCurrentSignaturePolicyImplied());
				if (signaturePolicyImplied != null) {
					xadesSignaturePolicy = new XAdESSignaturePolicy();
				}
				
			}
		}
		return xadesSignaturePolicy;
	}

	private UserNotice buildSPUserNotice(Element spUserNoticeElement) {
		try {
			final UserNotice userNotice = new UserNotice();

			final Element organization = DomUtils.getElement(spUserNoticeElement, xadesPath.getCurrentSPUserNoticeNoticeRefOrganization());
			if (organization != null) {
				userNotice.setOrganization(organization.getTextContent());
			}
			final Element noticeNumbers = DomUtils.getElement(spUserNoticeElement, xadesPath.getCurrentSPUserNoticeNoticeRefNoticeNumbers());
			if (noticeNumbers != null && noticeNumbers.hasChildNodes()) {
				final List noticeNumbersList = new ArrayList<>();
				NodeList childNodes = noticeNumbers.getChildNodes();
				for (int ii = 0; ii < childNodes.getLength(); ii++) {
					Node child = childNodes.item(ii);
					if (Node.ELEMENT_NODE == child.getNodeType() && XAdES132Element.INT.isSameTagName(child.getLocalName())) {
						noticeNumbersList.add(Integer.valueOf(child.getTextContent()));
					}
				}
				userNotice.setNoticeNumbers(noticeNumbersList.stream().mapToInt(i -> i).toArray());
			}
			final Element explicitText = DomUtils.getElement(spUserNoticeElement, xadesPath.getCurrentSPUserNoticeExplicitText());
			if (explicitText != null) {
				userNotice.setExplicitText(explicitText.getTextContent());
			}

			return userNotice;

		} catch (Exception e) {
			LOG.warn("Unable to build SPUserNotice qualifier. Reason : {}", e.getMessage(), e);
			return null;
		}
	}

	private boolean isHashComputationAsInPolicySpecification(Element transforms) {
		if (transforms != null && transforms.hasChildNodes()) {
			NodeList transformList = DomUtils.getNodeList(transforms, XMLDSigPath.TRANSFORM_PATH);
			if (transformList.getLength() == 1) {
				Node transform = transformList.item(0);
				String algorithm = DomUtils.getValue(transform, "@Algorithm");
				if (DSSXMLUtils.SP_DOC_DIGEST_AS_IN_SPECIFICATION_ALGORITHM_URI.equals(algorithm)) {
					return true;
				}
			}
		}
		return false;
	}

	@Override
	public SignatureProductionPlace getSignatureProductionPlace() {

		NodeList nodeList = DomUtils.getNodeList(signatureElement, xadesPath.getSignatureProductionPlacePath());
		if ((nodeList.getLength() == 0) || (nodeList.item(0) == null)) {
			String signatureProductionPlaceV2Path = xadesPath.getSignatureProductionPlaceV2Path();
			if (signatureProductionPlaceV2Path != null) {
				nodeList = DomUtils.getNodeList(signatureElement, signatureProductionPlaceV2Path);
			}
		}
		if ((nodeList.getLength() == 0) || (nodeList.item(0) == null)) {
			return null;
		}
		final SignatureProductionPlace signatureProductionPlace = new SignatureProductionPlace();
		final NodeList list = nodeList.item(0).getChildNodes();
		for (int ii = 0; ii < list.getLength(); ii++) {

			final Node item = list.item(ii);
			final String name = item.getLocalName();
			final String nodeValue = item.getTextContent();
			if (XAdES132Element.CITY.isSameTagName(name)) {
				signatureProductionPlace.setCity(nodeValue);
			} else if (XAdES132Element.STATE_OR_PROVINCE.isSameTagName(name)) {
				signatureProductionPlace.setStateOrProvince(nodeValue);
			} else if (XAdES132Element.POSTAL_CODE.isSameTagName(name)) {
				signatureProductionPlace.setPostalCode(nodeValue);
			} else if (XAdES132Element.COUNTRY_NAME.isSameTagName(name)) {
				signatureProductionPlace.setCountryName(nodeValue);
			} else if (XAdES132Element.STREET_ADDRESS.isSameTagName(name)) {
				signatureProductionPlace.setStreetAddress(nodeValue);
			}
		}
		return signatureProductionPlace;
	}

	@Override
	public SignaturePolicyStore getSignaturePolicyStore() {
		String signaturePolicyStorePath = xadesPath.getSignaturePolicyStorePath();
		if (Utils.isStringNotEmpty(signaturePolicyStorePath)) {
			NodeList nodeList = DomUtils.getNodeList(signatureElement, signaturePolicyStorePath);
			if (nodeList.getLength() > 0) {
				SignaturePolicyStore sps = new SignaturePolicyStore();
				
				Element signaturePolicyStoreElement = (Element) nodeList.item(0);
				String id = signaturePolicyStoreElement.getAttribute(XMLDSigAttribute.ID.getAttributeName());
				if (Utils.isStringNotEmpty(id)) {
					sps.setId(id);
				}

				SpDocSpecification spDocSpec = null;
				String currentSPDocSpecificationPath = xadesPath.getCurrentSPDocSpecification();
				if (Utils.isStringNotEmpty(currentSPDocSpecificationPath)) {
					Element spDocSpecificationElement = DomUtils.getElement(signaturePolicyStoreElement, currentSPDocSpecificationPath);
					if (spDocSpecificationElement != null) {
						spDocSpec = buildSpDocSpecification(spDocSpecificationElement);
					}
				}
				sps.setSpDocSpecification(spDocSpec);

				String currentSignaturePolicyDocumentPath = xadesPath.getCurrentSignaturePolicyDocument();
				if (Utils.isStringNotEmpty(currentSignaturePolicyDocumentPath)) {
					String spDocB64 = DomUtils.getValue(signaturePolicyStoreElement, currentSignaturePolicyDocumentPath);
					if (Utils.isStringNotEmpty(spDocB64) && Utils.isBase64Encoded(spDocB64)) {
						sps.setSignaturePolicyContent(new InMemoryDocument(Utils.fromBase64(spDocB64)));
					}
				}

				String currentSigPolDocLocalURI = xadesPath.getCurrentSigPolDocLocalURI();
				if (Utils.isStringNotEmpty(currentSigPolDocLocalURI)) {
					String sigPolDocLocalURI = DomUtils.getValue(signaturePolicyStoreElement, currentSigPolDocLocalURI);
					if (Utils.isStringNotEmpty(sigPolDocLocalURI)) {
						sps.setSigPolDocLocalURI(sigPolDocLocalURI);
					}
				}

				return sps;
			}
		}
		return null;
	}

	private SpDocSpecification buildSpDocSpecification(Element spDocSpecificationElement) {
		SpDocSpecification spDocSpec = new SpDocSpecification();

		Element identifierElement = DomUtils.getElement(spDocSpecificationElement, xadesPath.getCurrentIdentifier());
		if (identifierElement != null) {
			String spDocSpecId = identifierElement.getTextContent();

			ObjectIdentifierQualifier qualifier = null;
			String qualifierString = identifierElement.getAttribute(XAdES132Attribute.QUALIFIER.getAttributeName());
			if (Utils.isStringNotBlank(qualifierString)) {
				qualifier = ObjectIdentifierQualifier.fromValue(qualifierString);
				spDocSpec.setQualifier(qualifier);
			}

			spDocSpec.setId(DSSUtils.getObjectIdentifierValue(spDocSpecId, qualifier));
		}

		String description = DomUtils.getValue(spDocSpecificationElement, xadesPath.getCurrentDescription());
		if (Utils.isStringNotBlank(description)) {
			spDocSpec.setDescription(description);
		}

		String currentDocumentationReferenceElementsPath = xadesPath.getCurrentDocumentationReferenceElements();
		if (Utils.isStringNotEmpty(currentDocumentationReferenceElementsPath)) {
			String[] documentationReferences = null;
			NodeList documentReferenceList = DomUtils.getNodeList(spDocSpecificationElement, currentDocumentationReferenceElementsPath);
			if (documentReferenceList != null && documentReferenceList.getLength() > 0) {
				documentationReferences = new String[documentReferenceList.getLength()];
				for (int i = 0; i < documentReferenceList.getLength(); i++) {
					documentationReferences[i] = documentReferenceList.item(i).getTextContent();
				}
			}
			spDocSpec.setDocumentationReferences(documentationReferences);
		}

		return spDocSpec;
	}

	@Override
	public List getSignedAssertions() {
		List result = new ArrayList<>();
		String signedAssertionPath = xadesPath.getSignedAssertionPath();
		if (signedAssertionPath != null) {
			NodeList nodeList = DomUtils.getNodeList(signatureElement, signedAssertionPath);
			for (int ii = 0; ii < nodeList.getLength(); ii++) {
				result.add(new SignerRole(DomUtils.xmlToString(nodeList.item(ii).getFirstChild()), EndorsementType.SIGNED));
			}
		}
		return result;
	}

	@Override
	public List getClaimedSignerRoles() {
		NodeList nodeList = DomUtils.getNodeList(signatureElement, xadesPath.getClaimedRolePath());
		if (nodeList.getLength() == 0) {
			String claimedRoleV2Path = xadesPath.getClaimedRoleV2Path();
			if (claimedRoleV2Path != null) {
				nodeList = DomUtils.getNodeList(signatureElement, claimedRoleV2Path);
				if (nodeList.getLength() == 0) {
					return Collections.emptyList();
				}
			}
		}
		List claimedRoles = new ArrayList<>();
		for (int ii = 0; ii < nodeList.getLength(); ii++) {
			claimedRoles.add(new SignerRole(nodeList.item(ii).getTextContent(), EndorsementType.CLAIMED));
		}
		return claimedRoles;
	}

	@Override
	public List getCertifiedSignerRoles() {
		/**
		 * 
		 * 
		 * 
		 * 
		 * 
		 * 
		 * 
		 * 
		 * 
		 * 
		 * 
		 */
		NodeList nodeList = DomUtils.getNodeList(signatureElement, xadesPath.getCertifiedRolePath());
		if (nodeList.getLength() == 0) {
			String certifiedRoleV2Path = xadesPath.getCertifiedRoleV2Path();
			if (certifiedRoleV2Path != null) {
				nodeList = DomUtils.getNodeList(signatureElement, certifiedRoleV2Path);
				if (nodeList.getLength() == 0) {
					return Collections.emptyList();
				}
			}
		}
		final List certifiedRoles = new ArrayList<>();
		for (int ii = 0; ii < nodeList.getLength(); ii++) {
			final Element certEl = (Element) nodeList.item(ii);
			final String textContent = certEl.getTextContent();
			certifiedRoles.add(new SignerRole(textContent, EndorsementType.CERTIFIED));
		}
		return certifiedRoles;
	}

	@Override
	public String getContentType() {
		String contentType = null;
		final NodeList allContentTypes = DomUtils.getNodeList(signatureElement, xadesPath.getDataObjectFormatObjectIdentifier());
		if (allContentTypes != null && allContentTypes.getLength() > 0) {
			for (int i = 0; i < allContentTypes.getLength(); i++) {
				Node node = allContentTypes.item(i);
				if (node instanceof Element) {
					Element element = (Element) node;
					contentType = element.getTextContent();
					// TODO returns the first one
					break;
				}
			}
		}
		return contentType;
	}

	@Override
	public String getMimeType() {
		String mimeType = null;
		final NodeList allMimeTypes = DomUtils.getNodeList(signatureElement, xadesPath.getDataObjectFormatMimeType());
		if (allMimeTypes != null && allMimeTypes.getLength() > 0) {
			for (int i = 0; i < allMimeTypes.getLength(); i++) {
				Node node = allMimeTypes.item(i);
				if (node instanceof Element) {
					Element element = (Element) node;
					mimeType = element.getTextContent();
					// TODO returns the first one
					break;
				}
			}
		}
		return mimeType;
	}
	
	/**
	 * Returns a base64 SignatureValue
	 * 
	 * @return base64 {@link String}
	 */
	public String getSignatureValueBase64() {
		Element signatureValueElement = DomUtils.getElement(signatureElement, XMLDSigPath.SIGNATURE_VALUE_PATH);
		if (signatureValueElement != null) {
			return signatureValueElement.getTextContent();
		}
		return null;
	}

	@Override
	public byte[] getSignatureValue() {
		String signatureValueBase64 = getSignatureValueBase64();
		if (signatureValueBase64 != null && Utils.isBase64Encoded(signatureValueBase64)) {
			return Utils.fromBase64(signatureValueBase64);
		} else {
			if (LOG.isDebugEnabled()) {
				LOG.warn("The signature value is not represented by a base64-encoded string! Found value : '{}'", signatureValueBase64);
			} else {
				LOG.warn("The signature value is not represented by a base64-encoded string!");
			}
		}
		return null;
	}

	/**
	 * Returns Id of the ds:SignatureValue element
	 *
	 * @return {@link String} Id
	 */
	public String getSignatureValueId() {
		return DomUtils.getValue(signatureElement, XMLDSigPath.SIGNATURE_VALUE_ID_PATH);
	}

	/**
	 * This method returns the list of ds:Object elements for the current signature element.
	 *
	 * @return {@link NodeList}
	 */
	public NodeList getObjects() {
		return DomUtils.getNodeList(signatureElement, XMLDSigPath.OBJECT_PATH);
	}

	/**
	 * Gets xades:CompleteCertificateRefs or xades141:CompleteCertificateRefsV2 element
	 *
	 * @return {@link Element}
	 */
	public Element getCompleteCertificateRefs() {
		Element element = null;
		String completeCertificateRefsPath = xadesPath.getCompleteCertificateRefsPath();
		if (Utils.isStringNotEmpty(completeCertificateRefsPath)) {
			element = DomUtils.getElement(signatureElement, completeCertificateRefsPath);
		}
		String completeCertificateRefsV2Path = xadesPath.getCompleteCertificateRefsV2Path();
		if (element == null && Utils.isStringNotEmpty(completeCertificateRefsV2Path)) {
			element = DomUtils.getElement(signatureElement, completeCertificateRefsV2Path);
		}
		return element;
	}

	/**
	 * Gets xades:CompleteRevocationRefs
	 *
	 * @return {@link Element}
	 */
	public Element getCompleteRevocationRefs() {
		return DomUtils.getElement(signatureElement, xadesPath.getCompleteRevocationRefsPath());
	}

	/**
	 * Gets xades:SigAndRefsTimeStamp node list
	 *
	 * @return {@link NodeList}
	 */
	public NodeList getSigAndRefsTimeStamp() {
		NodeList nodeList = null;
		String sigAndRefsTimestampPath = xadesPath.getSigAndRefsTimestampPath();
		if (Utils.isStringNotEmpty(sigAndRefsTimestampPath)) {
			nodeList = DomUtils.getNodeList(signatureElement, sigAndRefsTimestampPath);
		}
		String sigAndRefsTimestampV2Path = xadesPath.getSigAndRefsTimestampV2Path();
		if ((nodeList == null || nodeList.getLength() == 0) && Utils.isStringNotEmpty(sigAndRefsTimestampV2Path)) {
			nodeList = DomUtils.getNodeList(signatureElement, sigAndRefsTimestampV2Path);
		}
		return nodeList;
	}

	/**
	 * Gets xades:RefsOnlyTimestamp node list
	 *
	 * @return {@link NodeList}
	 */
	public NodeList getRefsOnlyTimestampTimeStamp() {
		NodeList nodeList = null;
		String refsOnlyTimestampPath = xadesPath.getRefsOnlyTimestampPath();
		if (Utils.isStringNotEmpty(refsOnlyTimestampPath)) {
			nodeList = DomUtils.getNodeList(signatureElement, refsOnlyTimestampPath);
		}
		String refsOnlyTimestampV2Path = xadesPath.getRefsOnlyTimestampV2Path();
		if ((nodeList == null || nodeList.getLength() == 0) && Utils.isStringNotEmpty(refsOnlyTimestampV2Path)) {
			nodeList = DomUtils.getNodeList(signatureElement, refsOnlyTimestampV2Path);
		}
		return nodeList;
	}

	/**
	 * Gets xades:CertificateValues element
	 *
	 * @return {@link Element}
	 */
	public Element getCertificateValues() {
		return DomUtils.getElement(signatureElement, xadesPath.getCertificateValuesPath());
	}

	/**
	 * Gets xades:RevocationValues element
	 *
	 * @return {@link Element}
	 */
	public Element getRevocationValues() {
		return DomUtils.getElement(signatureElement, xadesPath.getRevocationValuesPath());
	}

	@Override
	public void addExternalTimestamp(TimestampToken timestamp) {
		throw new UnsupportedOperationException("The action is not supported for XAdES!");
	}

	@Override
	protected XAdESBaselineRequirementsChecker getBaselineRequirementsChecker() {
		return (XAdESBaselineRequirementsChecker) super.getBaselineRequirementsChecker();
	}

	@Override
	protected XAdESBaselineRequirementsChecker createBaselineRequirementsChecker(CertificateVerifier certificateVerifier) {
		return new XAdESBaselineRequirementsChecker(this, certificateVerifier);
	}

	@Override
	public void checkSignatureIntegrity() {
		if (signatureCryptographicVerification != null) {
			return;
		}
		signatureCryptographicVerification = new SignatureCryptographicVerification();
		try {
			final XMLSignature currentSantuarioSignature = getSantuarioSignature();
			CandidatesForSigningCertificate candidatesForSigningCertificate = getCandidatesForSigningCertificate();
			
			SignatureIntegrityValidator signingCertificateValidator = new XAdESSignatureIntegrityValidator(currentSantuarioSignature);
			CertificateValidity certificateValidity = signingCertificateValidator.validate(candidatesForSigningCertificate);
			if (certificateValidity != null) {
				candidatesForSigningCertificate.setTheCertificateValidity(certificateValidity);
			}
			
			List errorMessages = signingCertificateValidator.getErrorMessages();
			signatureCryptographicVerification.setErrorMessages(errorMessages);

			boolean allReferenceDataFound = true;
			boolean allReferenceDataIntact = true;
			List refValidations = getReferenceValidations();
			for (ReferenceValidation referenceValidation : refValidations) {
				allReferenceDataFound = allReferenceDataFound && referenceValidation.isFound();
				allReferenceDataIntact = allReferenceDataIntact && referenceValidation.isIntact();
			}

			signatureCryptographicVerification.setReferenceDataFound(allReferenceDataFound);
			signatureCryptographicVerification.setReferenceDataIntact(allReferenceDataIntact);
			signatureCryptographicVerification.setSignatureIntact(certificateValidity != null);
			
		} catch (Exception e) {
			String errorMessage = "checkSignatureIntegrity : {}";
			if (LOG.isDebugEnabled()) {
				LOG.warn(errorMessage, e.getMessage(), e);
			} else {
				LOG.warn(errorMessage, e.getMessage());
			}
			StackTraceElement[] stackTrace = e.getStackTrace();
			final String name = XAdESSignature.class.getName();
			int lineNumber = 0;
			for (StackTraceElement element : stackTrace) {

				final String className = element.getClassName();
				if (className.equals(name)) {

					lineNumber = element.getLineNumber();
					break;
				}
			}
			signatureCryptographicVerification.setErrorMessage(e.getMessage() + "/ XAdESSignature/Line number/" + lineNumber);
			
		}
	}

	@Override
	public List getReferenceValidations() {
		if (referenceValidations == null) {
			referenceValidations = new ArrayList<>();

			final XMLSignature currentSantuarioSignature = getSantuarioSignature();
			boolean atLeastOneReferenceElementFound = false;
			
			List santuarioReferences = getReferences();
			for (Reference reference : santuarioReferences) {
				XAdESReferenceValidation validation = new XAdESReferenceValidation(reference);
				validation.setType(DigestMatcherType.REFERENCE);

				referenceValidations.add(validation);

				boolean found = false;
				boolean intact = false;
				
				try {
					final Digest digest = DSSXMLUtils.getReferenceDigest(reference);
					validation.setDigest(digest);

					found = DSSXMLUtils.isAbleToDeReferenceContent(reference);

					final String uri = validation.getUri();
					boolean isDuplicated = DSSXMLUtils.isReferencedContentAmbiguous(
							signatureElement.getOwnerDocument(), uri);
					validation.setDuplicated(isDuplicated);
					
					boolean isElementReference = DomUtils.isElementReference(uri);
							
					if (isElementReference && DSSXMLUtils.isSignedProperties(reference, xadesPath)) {
						validation.setType(DigestMatcherType.SIGNED_PROPERTIES);
						found = found && (disableXSWProtection || findSignedPropertiesById(uri));
						
					} else if (DomUtils.isXPointerQuery(uri)) {
						validation.setType(DigestMatcherType.XPOINTER);
						// found is checked in the reference validation
						
					} else if (DSSXMLUtils.isCounterSignature(reference, xadesPath)) {
						validation.setType(DigestMatcherType.COUNTER_SIGNATURE);
						// found is checked in the reference validation
						XAdESSignature masterSignature = (XAdESSignature) getMasterSignature();
						if (masterSignature != null) {
							referenceValidations.add(getCounterSignatureReferenceValidation(reference, masterSignature));
						} else {
							LOG.warn("Master signature is not found! " +
									"Unable to verify counter signed SignatureValue for detached signatures.");
						}
						
					} else if (isElementReference && DSSXMLUtils.isKeyInfoReference(reference,
							currentSantuarioSignature.getElement())) {
						validation.setType(DigestMatcherType.KEY_INFO);
						found = true; // we check it in prior inside "isKeyInfoReference" method
						
					} else if (isElementReference && DSSXMLUtils.isSignaturePropertiesReference(reference,
							currentSantuarioSignature.getElement())) {
						validation.setType(DigestMatcherType.SIGNATURE_PROPERTIES);
						found = true; // Id is verified inside "isSignaturePropertiesReference" method
						
					} else if (isElementReference && reference.typeIsReferenceToObject()) {
						validation.setType(DigestMatcherType.OBJECT);
						found = found && (disableXSWProtection || findObjectById(uri));
						
					} else if (isElementReference && reference.typeIsReferenceToManifest()) {
						validation.setType(DigestMatcherType.MANIFEST);
						Element manifestElement = DSSXMLUtils.getManifestById(signatureElement, uri);
						found = found && (disableXSWProtection || (manifestElement != null));
						if (manifestElement != null) {
							validation.getDependentValidations().addAll(getManifestReferences(manifestElement));
						}
						
					}
					
					if (found && !isDuplicated) {
						intact = reference.verify();
					}

					if (LOG.isTraceEnabled()) {
						LOG.trace("Reference validation output: ");
						LOG.trace(new String(reference.getReferencedBytes()));
					}
					
				} catch (Exception e) {
					LOG.warn("Unable to verify reference with Id [{}] : {}", reference.getId(), e.getMessage(), e);
				}
				
				if (DigestMatcherType.REFERENCE.equals(validation.getType()) || DigestMatcherType.OBJECT.equals(validation.getType()) ||
						DigestMatcherType.MANIFEST.equals(validation.getType()) || DigestMatcherType.XPOINTER.equals(validation.getType()) ||
						DigestMatcherType.COUNTER_SIGNATURE.equals(validation.getType())) {
					atLeastOneReferenceElementFound = true;
				}
					
				validation.setFound(found);
				validation.setIntact(intact);
				
			}

			// If at least one reference is not found, we add an empty
			// referenceValidation
			if (!atLeastOneReferenceElementFound) {
				referenceValidations.add(notFound(DigestMatcherType.REFERENCE));
			}
			
			if (referenceValidations.size() < santuarioReferences.size()) {
				LOG.warn("Not all references were validated!");
			}
			
		}
		return referenceValidations;
	}

	private ReferenceValidation getCounterSignatureReferenceValidation(Reference counterSignatureReference,
																	   XAdESSignature masterSignature) {
		ReferenceValidation referenceValidation = new ReferenceValidation();
		referenceValidation.setType(DigestMatcherType.COUNTER_SIGNED_SIGNATURE_VALUE);

		String masterSignatureValueBase64 = masterSignature.getSignatureValueBase64();
		if (Utils.isStringNotEmpty(masterSignatureValueBase64)) {
			referenceValidation.setFound(true);

			try {
				byte[] referencedBytes = counterSignatureReference.getContentsAfterTransformation().getBytes();
				Document document = DomUtils.buildDOM(referencedBytes);
				Element referencedElement = document.getDocumentElement();
				if (XMLDSigElement.SIGNATURE_VALUE.isSameTagName(referencedElement.getLocalName())) {
					String referencedSignatureValueBase64 = referencedElement.getTextContent();
					boolean intact = Utils.areStringsEqual(masterSignatureValueBase64, referencedSignatureValueBase64);
					if (!intact) {
						LOG.warn("The referenced counter signed value does not match " +
								"the master signature's ds:SignatureValue content!");
					}
					referenceValidation.setIntact(intact);

				} else {
					LOG.warn("The counter signature reference does not result to a ds:SignatureValue element!");
				}

			} catch (Exception e) {
				LOG.warn("Unable to verify the counter signed reference! Reason : {}", e.getMessage(), e);
			}

		} else {
			LOG.warn("Master signature's ds:SignatureValue element does not contain data!");
		}

		return referenceValidation;
	}

	/**
	 * TS 119 442 - V1.1.1 - Electronic Signatures and Infrastructures (ESI), ch. 5.1.4.2.1.3 XML component:
	 * 
	 * In case of XAdES signatures, the input of the digest value computation shall be the result of applying the
	 * canonicalization algorithm identified within the CanonicalizationMethod child element's value to the
	 * corresponding ds:Signature element and its contents. The canonicalization shall be computed keeping this
	 * ds:Signature element as a descendant of the XML root element, without detaching it.
	 */
	@Override
	public SignatureDigestReference getSignatureDigestReference(DigestAlgorithm digestAlgorithm) {
		byte[] signatureElementBytes = XMLCanonicalizer.createInstance(DEFAULT_CANONICALIZATION_METHOD).canonicalize(signatureElement);
		byte[] digestValue = DSSUtils.digest(digestAlgorithm, signatureElementBytes);
		return new SignatureDigestReference(DEFAULT_CANONICALIZATION_METHOD, new Digest(digestAlgorithm, digestValue));
	}

	@Override
	public Digest getDataToBeSignedRepresentation() {
		DigestAlgorithm digestAlgorithm = getDigestAlgorithm();
		if (digestAlgorithm == null) {
			LOG.warn("DigestAlgorithm is not found! Unable to compute DTBSR.");
			return null;
		}
		final Element signedInfo = getSignedInfo();
		if (signedInfo == null) {
			LOG.warn("SignedInfo element is not found! Unable to compute DTBSR.");
			return null;
		}
		String canonicalizationMethod = DomUtils.getValue(signedInfo, XMLDSigPath.CANONICALIZATION_ALGORITHM_PATH);
		if (Utils.isStringEmpty(canonicalizationMethod)) {
			LOG.warn("Canonicalization method is not present in SignedInfo element! Unable to compute DTBSR.");
			return null;
		}
		byte[] canonicalizedSignedInfo = XMLCanonicalizer.createInstance(canonicalizationMethod).canonicalize(signedInfo);
		return new Digest(digestAlgorithm, DSSUtils.digest(digestAlgorithm, canonicalizedSignedInfo));
	}

	/**
	 * Returns the ds:SignedInfo element
	 *
	 * @return {@link Element} ds:SignedInfo
	 */
	public Element getSignedInfo() {
		try {
			return DomUtils.getElement(signatureElement, XMLDSigPath.SIGNED_INFO_PATH);
		} catch (DSSException e) {
			LOG.warn(String.format("Unable to extract ds:SignedInfo element! Reason : %s.", e.getMessage()), e);
			return null;
		}
	}
	
	/**
	 * Returns a list of all references contained in the given manifest
	 * @param manifestElement {@link Element} to get references from
	 * @return list of {@link ReferenceValidation} objects
	 */
	private List getManifestReferences(Element manifestElement) {
		ManifestValidator mv = new ManifestValidator(manifestElement, detachedContents);
		return mv.validate();
	}

	private boolean findSignedPropertiesById(String uri) {
		return getSignedPropertiesById(uri) != null;
	}

	private Node getSignedPropertiesById(String uri) {
		if (Utils.isStringNotBlank(uri)) {
			String signedPropertiesById = xadesPath.getSignedPropertiesPath() + DomUtils.getXPathByIdAttribute(uri);
			return DomUtils.getNode(signatureElement, signedPropertiesById);
		}
		return null;
	}

	private boolean findObjectById(String uri) {
		return getObjectById(uri) != null;
	}

	/**
	 * Gets ds:Object by its Id
	 *
	 * @param id {@link String} object Id
	 * @return {@link Node}
	 */
	public Node getObjectById(String id) {
		if (Utils.isStringNotBlank(id)) {
			String objectById = XMLDSigPath.OBJECT_PATH + DomUtils.getXPathByIdAttribute(id);
			return DomUtils.getNode(signatureElement, objectById);
		}
		return null;
	}

	/**
	 * Gets ds:Manifest by its Id
	 *
	 * @param id {@link String} manifest Id
	 * @return {@link Element} Manifest
	 */
	public Element getManifestById(String id) {
		if (Utils.isStringNotBlank(id)) {
			String manifestById = XMLDSigPath.MANIFEST_PATH + DomUtils.getXPathByIdAttribute(id);
			return DomUtils.getElement(signatureElement, manifestById);
		}
		return null;
	}

	private ReferenceValidation notFound(DigestMatcherType type) {
		ReferenceValidation validation = new ReferenceValidation();
		validation.setType(type);
		validation.setFound(false);
		return validation;
	}

	private XMLSignature getSantuarioSignature() {
		if (santuarioSignature != null) {
			return santuarioSignature;
		}
		try {
			final Document document = signatureElement.getOwnerDocument();
			final Element rootElement = document.getDocumentElement();

			DSSXMLUtils.setIDIdentifier(rootElement);
			DSSXMLUtils.recursiveIdBrowse(rootElement);

			// Secure validation disabled to support all signature algos
			santuarioSignature = new XMLSignature(signatureElement, "", false);
			if (Utils.isCollectionNotEmpty(detachedContents)) {
				initDetachedSignatureResolvers(detachedContents);
				initCounterSignatureResolver(detachedContents);
			}
			return santuarioSignature;
		} catch (XMLSecurityException e) {
			throw new DSSException(String.format("Unable to initialize Santuario XMLSignature. Reason : %s", e.getMessage()), e);
		}
	}

	private void initDetachedSignatureResolvers(List detachedContents) {
		Element signedInfo = getSignedInfo();
		if (signedInfo != null) {
			XMLSignature xmlSignature = getSantuarioSignature();
			for (DigestAlgorithm digestAlgorithm : DSSXMLUtils.getReferenceDigestAlgos(signedInfo)) {
				xmlSignature.addResourceResolver(new DetachedSignatureResolver(detachedContents, digestAlgorithm));
			}
		}
	}
	
	/**
	 * Used for a counter signature extension only
	 */
	private void initCounterSignatureResolver(List detachedContents) {
		Element signedInfo = getSignedInfo();
		if (signedInfo != null) {
			XMLSignature xmlSignature = getSantuarioSignature();
			List types = DSSXMLUtils.getReferenceTypes(signedInfo);
			for (String type : types) {
				if (xadesPath.getCounterSignatureUri().equals(type)) {
					for (DSSDocument document : detachedContents) {
						// only one SignatureValue document shall be provided
						if (isDetachedSignatureValueDocument(document)) {
							xmlSignature.addResourceResolver(new CounterSignatureResolver(document));
							break;
						}
					}
				}
			}
		}
	}
	
	private boolean isDetachedSignatureValueDocument(DSSDocument detachedContents) {
		try {
			if (DomUtils.isDOM(detachedContents)) {
				Document document = DomUtils.buildDOM(detachedContents);
				if (document != null) {
					Node node = document.getChildNodes().item(0);
					return XMLDSigElement.SIGNATURE_VALUE.getTagName().equals(node.getLocalName());
				}
			}
		} catch (Exception e) {
			// continue
		}
		return false;
	}

	/**
	 * This method retrieves the potential countersignatures embedded in the XAdES signature document. From ETSI TS 101
	 * 903 v1.4.2:
	 *
	 * 7.2.4.1 Countersignature identifier in Type attribute of ds:Reference
	 *
	 * A XAdES signature containing a ds:Reference element whose Type attribute has value
	 * "http://uri.etsi.org/01903#CountersignedSignature" will indicate that
	 * is is, in fact, a countersignature of the signature referenced by this element.
	 *
	 * 7.2.4.2 Enveloped countersignatures: the CounterSignature element
	 *
	 * The CounterSignature is an unsigned property that qualifies the signature. A XAdES signature MAY have more than
	 * one CounterSignature properties. As
	 * indicated by its name, it contains one countersignature of the qualified signature.
	 *
	 * @return a list containing the countersignatures embedded in the XAdES signature document
	 */
	@Override
	public List getCounterSignatures() {
		if (counterSignatures != null) {
			return counterSignatures;
		}
		
		counterSignatures = new ArrayList<>();

		// see ETSI TS 101 903 V1.4.2 (2010-12) pp. 38/39/40
		final NodeList counterSignaturesElements = DomUtils.getNodeList(signatureElement,
				xadesPath.getCounterSignaturePath());
		if (counterSignaturesElements != null && counterSignaturesElements.getLength() > 0) {
			for (int ii = 0; ii < counterSignaturesElements.getLength(); ii++) {
				XAdESSignature counterSignature = DSSXMLUtils.createCounterSignature(
						(Element) counterSignaturesElements.item(ii), this);
				if (counterSignature != null) {
					counterSignatures.add(counterSignature);
				}
			}
		}
		return counterSignatures;
	}
	
	@Override
	protected SignatureIdentifierBuilder getSignatureIdentifierBuilder() {
		return new XAdESSignatureIdentifierBuilder(this);
	}
	
	@Override
	public String getDAIdentifier() {
		if (daIdentifier == null) {
			daIdentifier = DSSXMLUtils.getIDIdentifier(signatureElement);
		}
		return daIdentifier;
	}

	/**
	 * Retrieves the name of each node found under the UnsignedSignatureProperties element
	 *
	 * @return an ArrayList containing the retrieved node names
	 */
	public List getUnsignedSignatureProperties() {
		return DomUtils.getChildrenNames(signatureElement, xadesPath.getUnsignedSignaturePropertiesPath());
	}

	/**
	 * Retrieves the name of each node found under the SignedSignatureProperties element
	 *
	 * @return an ArrayList containing the retrieved node names
	 */
	public List getSignedSignatureProperties() {
		return DomUtils.getChildrenNames(signatureElement, xadesPath.getSignedSignaturePropertiesPath());
	}

	/**
	 * Retrieves the name of each node found under the SignedProperties element
	 *
	 * @return an ArrayList containing the retrieved node names
	 */
	public List getSignedProperties() {
		return DomUtils.getChildrenNames(signatureElement, xadesPath.getSignedPropertiesPath());
	}

	/**
	 * Retrieves the name of each node found under the UnsignedProperties element
	 *
	 * @return an ArrayList containing the retrieved node names
	 */
	public List getUnsignedProperties() {
		return DomUtils.getChildrenNames(signatureElement, xadesPath.getUnsignedPropertiesPath());
	}

	/**
	 * Retrieves the name of each node found under the SignedDataObjectProperties element
	 *
	 * @return an ArrayList containing the retrieved node names
	 */
	public List getSignedDataObjectProperties() {
		return DomUtils.getChildrenNames(signatureElement, xadesPath.getSignedDataObjectPropertiesPath());
	}

	@Override
	public SignatureLevel getDataFoundUpToLevel() {
		if (!hasBESProfile()) {
			return SignatureLevel.XML_NOT_ETSI;
		}

		boolean baselineProfile = hasBProfile();

		if (!hasExtendedTProfile()) {
			if (baselineProfile) {
				return SignatureLevel.XAdES_BASELINE_B;
			} else if (hasEPESProfile()) {
				return SignatureLevel.XAdES_EPES;
			}
			return SignatureLevel.XAdES_BES;
		}

		baselineProfile = baselineProfile && hasTProfile();

		if (baselineProfile && hasLTProfile()) {
			if (hasLTAProfile()) {
				return SignatureLevel.XAdES_BASELINE_LTA;
			}
			return SignatureLevel.XAdES_BASELINE_LT;

		} else if (hasCProfile()) {
			if (hasXLProfile()) {
				if (hasAProfile()) {
					return SignatureLevel.XAdES_A;
				}
				if (hasXProfile()) {
					return SignatureLevel.XAdES_XL;
				}
			}
			if (hasXProfile()) {
				return SignatureLevel.XAdES_X;
			}
			return SignatureLevel.XAdES_C;

		} else if (hasXLProfile()) {
			if (hasAProfile()) {
				return SignatureLevel.XAdES_A; // XAdES-E-A can be built on XAdES-E-T directly
			}
			return SignatureLevel.XAdES_LT;
		}

		return baselineProfile ? SignatureLevel.XAdES_BASELINE_T : SignatureLevel.XAdES_T;
	}

	@Override
	public List validateStructure() {
		return DSSXMLUtils.validateAgainstXSD(xadesPath.getXSDUtils(), new DOMSource(signatureElement));
	}

	@Override
	protected List findSignatureScopes() {
		return new XAdESSignatureScopeFinder().findSignatureScope(this);
	}

	/**
	 * This method returns the last timestamp validation data for an archive
	 * timestamp.
	 *
	 * @return {@link Element} xades141:TimestampValidationData
	 */
	public Element getLastTimestampValidationData() {
		final NodeList nodeList = DomUtils.getNodeList(signatureElement, xadesPath.getUnsignedSignaturePropertiesPath() + "/*");
		if (nodeList.getLength() > 0) {
			final Element unsignedSignatureElement = (Element) nodeList.item(nodeList.getLength() - 1);
			final String nodeName = unsignedSignatureElement.getLocalName();
			if (XAdES141Element.TIMESTAMP_VALIDATION_DATA.isSameTagName(nodeName)) {
				return unsignedSignatureElement;
			}
		}
		return null;
	}

	@Override
	public List getCommitmentTypeIndications() {
		List result = null;
		NodeList nodeList = DomUtils.getNodeList(signatureElement, xadesPath.getCommitmentTypeIndicationPath());
		if (nodeList != null && nodeList.getLength() > 0) {
			result = new ArrayList<>();
			for (int ii = 0; ii < nodeList.getLength(); ii++) {
				Node commitmentTypeIndicationNode = nodeList.item(ii);
				Element identifier = DomUtils.getElement(commitmentTypeIndicationNode, xadesPath.getCurrentCommitmentIdentifierPath());

				String uri = identifier.getTextContent();
				if (uri == null) {
					LOG.warn("The Identifier for a CommitmentTypeIndication is not defined! The CommitmentType is skipped.");
					continue;
				}

				ObjectIdentifierQualifier qualifier = null;
				String qualifierString = identifier.getAttribute(XAdES132Attribute.QUALIFIER.getAttributeName());
				if (Utils.isStringNotBlank(qualifierString)) {
					qualifier = ObjectIdentifierQualifier.fromValue(qualifierString);
				}

				uri = DSSUtils.getObjectIdentifierValue(uri, qualifier);

				final CommitmentTypeIndication commitmentTypeIndication = new CommitmentTypeIndication(uri);
				
				final Element descriptionNode = DomUtils.getElement(commitmentTypeIndicationNode,
						xadesPath.getCurrentCommitmentDescriptionPath());
				if (descriptionNode != null) {
					commitmentTypeIndication.setDescription(descriptionNode.getTextContent());
				}
				final Element docRefsNode = DomUtils.getElement(commitmentTypeIndicationNode,
						xadesPath.getCurrentCommitmentDocumentationReferencesPath());
				if (docRefsNode != null) {
					commitmentTypeIndication.setDocumentReferences(getDocumentationReferences(docRefsNode));
				}

				final Element allSignedDataObjectsNode = DomUtils.getElement(commitmentTypeIndicationNode,
						xadesPath.getCurrentCommitmentAllSignedDataObjectsPath());
				if (allSignedDataObjectsNode != null) {
					commitmentTypeIndication.setAllDataSignedObjects(true);
				} else {
					final NodeList commitmentObjectReferencesNodeList = DomUtils.getNodeList(commitmentTypeIndicationNode,
							xadesPath.getCurrentCommitmentObjectReferencesPath());
					if (commitmentObjectReferencesNodeList != null && commitmentObjectReferencesNodeList.getLength() > 0) {
						commitmentTypeIndication.setObjectReferences(getObjectReferences(commitmentObjectReferencesNodeList));
					}
				}

				result.add(commitmentTypeIndication);
			}
		}
		return result;
	}
	
	private List getDocumentationReferences(Element docRefsNode) {
		final NodeList docRefsChildNodes = DomUtils.getNodeList(docRefsNode, xadesPath.getCurrentDocumentationReference());
		if (docRefsChildNodes.getLength() > 0) {
			List docRefs = new ArrayList<>();
			for (int jj = 0; jj < docRefsChildNodes.getLength(); jj++) {
				Node docRefNode = docRefsChildNodes.item(jj);
				docRefs.add(docRefNode.getTextContent());
			}
			return docRefs;
		}
		return null;
	}

	private List getObjectReferences(NodeList commitmentObjectReferencesNodeList) {
		List signedDataObjects = new ArrayList<>();
		for (int i = 0; i < commitmentObjectReferencesNodeList.getLength(); i++) {
			signedDataObjects.add(DomUtils.getId(commitmentObjectReferencesNodeList.item(i).getTextContent()));
		}
		return signedDataObjects;
	}

	/**
	 * Gets a list of found references
	 *
	 * @return a list of {@link Reference}s
	 */
	public List getReferences() {
		if (references == null) {
			XMLSignature xmlSignature = getSantuarioSignature();
			SignedInfo signedInfo = xmlSignature.getSignedInfo();
            references = DSSXMLUtils.extractReferences(signedInfo);
		}
		return references;
	}

	/**
	 * Gets a list of found signature ds:Object elements
	 *
	 * @return a list of {@link Element}s
	 */
	public List getSignatureObjects() {
		final NodeList list = DomUtils.getNodeList(signatureElement, XMLDSigPath.OBJECT_PATH);
		final List objectElements = new ArrayList<>(list.getLength());
		for (int ii = 0; ii < list.getLength(); ii++) {
			final Node node = list.item(ii);
			final Element element = (Element) node;
			if (DomUtils.getElement(element, xadesPath.getSignedPropertiesPath()) != null) {
				// ignore signed properties
				continue;
			}
			objectElements.add(element);
		}
		return objectElements;
	}

	/**
	 * This method allows to register a new {@code XAdESPaths}.
	 *
	 * @param xadesPaths
	 *                   {@code XAdESPaths} to register
	 */
	public void registerXAdESPaths(final XAdESPath xadesPaths) {
		xadesPathHolders.add(xadesPaths);
	}

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy