eu.europa.esig.dss.validation.SignedDocumentValidator.java Source code

Java tutorial

Introduction

Here is the source code for eu.europa.esig.dss.validation.SignedDocumentValidator.java

Source

/**
 * 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.validation;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Constructor;
import java.net.URL;
import java.security.PublicKey;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Properties;
import java.util.Set;
import java.util.concurrent.TimeUnit;

import javax.security.auth.x500.X500Principal;

import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang.StringUtils;
import org.bouncycastle.asn1.ASN1OctetString;
import org.bouncycastle.asn1.ASN1Sequence;
import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
import org.bouncycastle.asn1.x509.qualified.ETSIQCObjectIdentifiers;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Document;

import eu.europa.esig.dss.DSSASN1Utils;
import eu.europa.esig.dss.DSSDocument;
import eu.europa.esig.dss.DSSException;
import eu.europa.esig.dss.DSSPKUtils;
import eu.europa.esig.dss.DSSUnsupportedOperationException;
import eu.europa.esig.dss.DSSUtils;
import eu.europa.esig.dss.DSSXMLUtils;
import eu.europa.esig.dss.DigestAlgorithm;
import eu.europa.esig.dss.EncryptionAlgorithm;
import eu.europa.esig.dss.OID;
import eu.europa.esig.dss.SignatureAlgorithm;
import eu.europa.esig.dss.SignatureLevel;
import eu.europa.esig.dss.TokenIdentifier;
import eu.europa.esig.dss.client.http.DataLoader;
import eu.europa.esig.dss.jaxb.diagnostic.DiagnosticData;
import eu.europa.esig.dss.jaxb.diagnostic.ObjectFactory;
import eu.europa.esig.dss.jaxb.diagnostic.XmlBasicSignatureType;
import eu.europa.esig.dss.jaxb.diagnostic.XmlCertificate;
import eu.europa.esig.dss.jaxb.diagnostic.XmlCertificateChainType;
import eu.europa.esig.dss.jaxb.diagnostic.XmlCertifiedRolesType;
import eu.europa.esig.dss.jaxb.diagnostic.XmlChainCertificate;
import eu.europa.esig.dss.jaxb.diagnostic.XmlClaimedRoles;
import eu.europa.esig.dss.jaxb.diagnostic.XmlCommitmentTypeIndication;
import eu.europa.esig.dss.jaxb.diagnostic.XmlDigestAlgAndValueType;
import eu.europa.esig.dss.jaxb.diagnostic.XmlDistinguishedName;
import eu.europa.esig.dss.jaxb.diagnostic.XmlInfoType;
import eu.europa.esig.dss.jaxb.diagnostic.XmlKeyUsageBits;
import eu.europa.esig.dss.jaxb.diagnostic.XmlMessage;
import eu.europa.esig.dss.jaxb.diagnostic.XmlPolicy;
import eu.europa.esig.dss.jaxb.diagnostic.XmlQCStatement;
import eu.europa.esig.dss.jaxb.diagnostic.XmlQualifiers;
import eu.europa.esig.dss.jaxb.diagnostic.XmlRevocationType;
import eu.europa.esig.dss.jaxb.diagnostic.XmlSignature;
import eu.europa.esig.dss.jaxb.diagnostic.XmlSignatureProductionPlace;
import eu.europa.esig.dss.jaxb.diagnostic.XmlSignatureScopeType;
import eu.europa.esig.dss.jaxb.diagnostic.XmlSignatureScopes;
import eu.europa.esig.dss.jaxb.diagnostic.XmlSignedObjectsType;
import eu.europa.esig.dss.jaxb.diagnostic.XmlSignedSignature;
import eu.europa.esig.dss.jaxb.diagnostic.XmlSigningCertificateType;
import eu.europa.esig.dss.jaxb.diagnostic.XmlStructuralValidationType;
import eu.europa.esig.dss.jaxb.diagnostic.XmlTimestampType;
import eu.europa.esig.dss.jaxb.diagnostic.XmlTimestamps;
import eu.europa.esig.dss.jaxb.diagnostic.XmlTrustedServiceProviderType;
import eu.europa.esig.dss.jaxb.diagnostic.XmlUsedCertificates;
import eu.europa.esig.dss.tsl.Condition;
import eu.europa.esig.dss.tsl.KeyUsageBit;
import eu.europa.esig.dss.tsl.PolicyIdCondition;
import eu.europa.esig.dss.tsl.QcStatementCondition;
import eu.europa.esig.dss.tsl.ServiceInfo;
import eu.europa.esig.dss.validation.policy.CustomProcessExecutor;
import eu.europa.esig.dss.validation.policy.EtsiValidationPolicy;
import eu.europa.esig.dss.validation.policy.ProcessExecutor;
import eu.europa.esig.dss.validation.policy.ValidationPolicy;
import eu.europa.esig.dss.validation.policy.rules.AttributeValue;
import eu.europa.esig.dss.validation.report.Reports;
import eu.europa.esig.dss.x509.CertificatePool;
import eu.europa.esig.dss.x509.CertificateSourceType;
import eu.europa.esig.dss.x509.CertificateToken;
import eu.europa.esig.dss.x509.RevocationToken;
import eu.europa.esig.dss.x509.SignaturePolicy;
import eu.europa.esig.dss.x509.TimestampType;
import eu.europa.esig.dss.x509.crl.ListCRLSource;
import eu.europa.esig.dss.x509.ocsp.ListOCSPSource;

/**
 * Validate the signed document. The content of the document is determined automatically. It can be: XML, CAdES(p7m), PDF or ASiC(zip).
 * SignatureScopeFinder can be set using the appropriate setter (ex. setCadesSignatureScopeFinder). By default, this class will use the
 * default SignatureScopeFinder as defined by eu.europa.esig.dss.validation.scope.SignatureScopeFinderFactory
 */
public abstract class SignedDocumentValidator implements DocumentValidator {

    private static final Logger LOG = LoggerFactory.getLogger(SignedDocumentValidator.class);

    /**
     * This variable can hold a specific {@code ProcessExecutor}
     */
    protected ProcessExecutor processExecutor = null;

    /*
     * The factory used to create DiagnosticData
     */
    protected static final ObjectFactory DIAGNOSTIC_DATA_OBJECT_FACTORY = new ObjectFactory();

    /**
     * This is the pool of certificates used in the validation process. The pools present in the certificate verifier are merged and added to this pool.
     */
    protected CertificatePool validationCertPool;

    /**
     * The document to validated (with the signature(s))
     */
    protected DSSDocument document;

    /**
     * In case of a detached signature this {@code List} contains the signed documents.
     */
    protected List<DSSDocument> detachedContents = new ArrayList<DSSDocument>();

    protected CertificateToken providedSigningCertificateToken = null;

    private ValidationPolicy countersignatureValidationPolicy;

    /**
     * The reference to the certificate verifier. The current DSS implementation proposes {@link eu.europa.esig.dss.validation.CommonCertificateVerifier}. This verifier
     * encapsulates the references to different sources used in the signature validation process.
     */
    protected CertificateVerifier certificateVerifier;

    private final SignatureScopeFinder signatureScopeFinder;

    /**
     * This list contains the list of signatures
     */
    protected List<AdvancedSignature> signatures = null;

    /**
     * This variable contains the reference to the diagnostic data.
     */
    protected DiagnosticData jaxbDiagnosticData; // JAXB object

    private final Condition qcp = new PolicyIdCondition(OID.id_etsi_qcp_public.getId());

    private final Condition qcpPlus = new PolicyIdCondition(OID.id_etsi_qcp_public_with_sscd.getId());

    private final Condition qcCompliance = new QcStatementCondition(
            ETSIQCObjectIdentifiers.id_etsi_qcs_QcCompliance.getId());

    private final Condition qcsscd = new QcStatementCondition(ETSIQCObjectIdentifiers.id_etsi_qcs_QcSSCD.getId());

    // Single policy document to use with all signatures.
    private File policyDocument;

    private HashMap<String, File> policyDocuments;

    private static List<Class<SignedDocumentValidator>> registredDocumentValidators = new ArrayList<Class<SignedDocumentValidator>>();

    static {
        Properties properties = new Properties();
        try {
            properties.load(SignedDocumentValidator.class.getResourceAsStream("/document-validators.properties"));
        } catch (IOException e) {
            LOG.error("Cannot load properties from document-validators.properties : " + e.getMessage(), e);
        }
        for (String propName : properties.stringPropertyNames()) {
            registerDocumentValidator(propName, properties.getProperty(propName));
        }
    }

    private static void registerDocumentValidator(String type, String clazzToFind) {
        try {
            @SuppressWarnings("unchecked")
            Class<SignedDocumentValidator> documentValidator = (Class<SignedDocumentValidator>) Class
                    .forName(clazzToFind);
            registredDocumentValidators.add(documentValidator);
            LOG.info("Validator '" + documentValidator.getName() + "' is registred");
        } catch (ClassNotFoundException e) {
            LOG.warn("Validator not found for signature type " + type);
        }
    }

    protected SignedDocumentValidator(SignatureScopeFinder signatureScopeFinder) {
        this.signatureScopeFinder = signatureScopeFinder;
    }

    /**
     * This method guesses the document format and returns an appropriate document validator.
     *
     * @param dssDocument
     *            The instance of {@code DSSDocument} to validate
     * @return returns the specific instance of SignedDocumentValidator in terms of the document type
     */
    public static SignedDocumentValidator fromDocument(final DSSDocument dssDocument) {
        if (CollectionUtils.isEmpty(registredDocumentValidators)) {
            throw new DSSException("No validator registred");
        }

        for (Class<SignedDocumentValidator> clazz : registredDocumentValidators) {
            try {
                Constructor<SignedDocumentValidator> defaultAndPrivateConstructor = clazz.getDeclaredConstructor();
                defaultAndPrivateConstructor.setAccessible(true);
                SignedDocumentValidator validator = defaultAndPrivateConstructor.newInstance();
                if (validator.isSupported(dssDocument)) {
                    Constructor<? extends SignedDocumentValidator> constructor = clazz
                            .getDeclaredConstructor(DSSDocument.class);
                    return constructor.newInstance(dssDocument);
                }
            } catch (Exception e) {
                LOG.error("Cannot instanciate class '" + clazz.getName() + "' : " + e.getMessage(), e);
            }
        }
        throw new DSSException("Document format not recognized/handled");
    }

    public abstract boolean isSupported(DSSDocument dssDocument);

    @Override
    public DSSDocument getDocument() {

        return document;
    }

    @Override
    public List<DSSDocument> getDetachedContents() {

        return detachedContents;
    }

    @Override
    public void defineSigningCertificate(final CertificateToken x509Certificate) {

        if (x509Certificate == null) {
            throw new NullPointerException();
        }
        providedSigningCertificateToken = validationCertPool.getInstance(x509Certificate,
                CertificateSourceType.OTHER);
    }

    /**
     * To carry out the validation process of the signature(s) some external sources of certificates and of revocation data can be needed. The certificate verifier is used to pass
     * these values. Note that once this setter is called any change in the content of the <code>CommonTrustedCertificateSource</code> or in adjunct certificate source is not
     * taken into account.
     *
     * @param certificateVerifier
     */
    @Override
    public void setCertificateVerifier(final CertificateVerifier certificateVerifier) {

        this.certificateVerifier = certificateVerifier;
        validationCertPool = certificateVerifier.createValidationPool();
    }

    @Override
    public void setDetachedContents(final List<DSSDocument> detachedContents) {

        this.detachedContents = detachedContents;
    }

    /**
     * This method allows to provide an external policy document to be used with all signatures within the document to validate.
     *
     * @param policyDocument
     */
    @Override
    public void setPolicyFile(final File policyDocument) {

        this.policyDocument = policyDocument;
    }

    /**
     * This method allows to provide an external policy document to be used with a given signature id.
     *
     * @param signatureId
     *            signature id
     * @param policyDocument
     */
    @Override
    public void setPolicyFile(final String signatureId, final File policyDocument) {

        if (policyDocuments == null) {

            policyDocuments = new HashMap<String, File>();
        }
        policyDocuments.put(signatureId, policyDocument);
    }

    /**
     * This setter allows to indicate the countersignature {@code ValidationPolicy} to be used.
     *
     * @param countersignatureValidationPolicy
     *            {@code ValidationPolicy} to be used
     */
    public void setCountersignatureValidationPolicy(final ValidationPolicy countersignatureValidationPolicy) {
        this.countersignatureValidationPolicy = countersignatureValidationPolicy;
    }

    @Override
    public Reports validateDocument() {

        return validateDocument((InputStream) null);
    }

    @Override
    public Reports validateDocument(final URL validationPolicyURL) {

        if (validationPolicyURL == null) {
            return validateDocument((InputStream) null);
        }
        try {
            return validateDocument(validationPolicyURL.openStream());
        } catch (IOException e) {
            throw new DSSException(e);
        }
    }

    @Override
    public Reports validateDocument(final String policyResourcePath) {

        if (policyResourcePath == null) {
            return validateDocument((InputStream) null);
        }
        return validateDocument(getClass().getResourceAsStream(policyResourcePath));
    }

    @Override
    public Reports validateDocument(final File policyFile) {

        if ((policyFile == null) || !policyFile.exists()) {
            return validateDocument((InputStream) null);
        }
        final InputStream inputStream = DSSUtils.toByteArrayInputStream(policyFile);
        return validateDocument(inputStream);
    }

    /**
     * Validates the document and all its signatures. The policyDataStream contains the constraint file. If null or empty the default file is used.
     *
     * @param policyDataStream
     *            {@code InputStream}
     */
    @Override
    public Reports validateDocument(final InputStream policyDataStream) {

        final Document validationPolicyDom = ValidationResourceManager.loadPolicyData(policyDataStream);
        return validateDocument(validationPolicyDom);
    }

    /**
     * Validates the document and all its signatures. The {@code validationPolicyDom} contains the constraint file. If null or empty the default file is used.
     *
     * @param validationPolicyDom
     *            {@code Document}
     * @return
     */
    @Override
    public Reports validateDocument(final Document validationPolicyDom) {

        final ValidationPolicy validationPolicy = new EtsiValidationPolicy(validationPolicyDom);
        return validateDocument(validationPolicy);
    }

    /**
     * Validates the document and all its signatures. The {@code validationPolicyDom} contains the constraint file. If null or empty the default file is used.
     *
     * @param validationPolicy
     *            {@code ValidationPolicy}
     * @return
     */
    @Override
    public Reports validateDocument(final ValidationPolicy validationPolicy) {

        LOG.info("Document validation...");
        if (certificateVerifier == null) {

            throw new NullPointerException();
        }
        Date date1 = null;
        if (LOG.isInfoEnabled()) {
            date1 = new Date();
        }
        final ProcessExecutor executor = provideProcessExecutorInstance();
        executor.setValidationPolicy(validationPolicy);
        if (countersignatureValidationPolicy == null) {

            final Document countersignaturePolicyData = ValidationResourceManager
                    .loadCountersignaturePolicyData(null);
            countersignatureValidationPolicy = new EtsiValidationPolicy(countersignaturePolicyData);
        }
        executor.setCountersignatureValidationPolicy(countersignatureValidationPolicy);

        final DiagnosticData jaxbDiagnosticData = generateDiagnosticData();

        final Document diagnosticDataDom = ValidationResourceManager.convert(jaxbDiagnosticData);
        executor.setDiagnosticDataDom(diagnosticDataDom);
        Date date2 = null;
        if (LOG.isTraceEnabled()) {

            date2 = new Date();
            final long dateDiff = DSSUtils.getDateDiff(date1, date2, TimeUnit.MILLISECONDS);
            LOG.trace("diff 1: " + dateDiff + " ms.");
        }
        final Reports reports = executor.execute();
        if (LOG.isTraceEnabled()) {

            Date date3 = new Date();
            final long dateDiff = DSSUtils.getDateDiff(date2, date3, TimeUnit.MILLISECONDS);
            LOG.trace("diff 2: " + dateDiff + " ms.");
        }
        return reports;
    }

    @Override
    public void setProcessExecutor(final ProcessExecutor processExecutor) {

        this.processExecutor = processExecutor;
    }

    /**
     * This method returns the process executor. If the instance of this class is not yet instantiated then the new instance is created.
     *
     * @return {@code ProcessExecutor}
     */
    public ProcessExecutor provideProcessExecutorInstance() {

        if (processExecutor == null) {
            processExecutor = new CustomProcessExecutor();
        }
        return processExecutor;
    }

    /**
     * This method generates the diagnostic data. This is the set of all data extracted from the signature, associated certificates and trusted lists. The diagnostic data contains
     * also the results of basic computations (hash check, signature integrity, certificates chain...
     */
    private DiagnosticData generateDiagnosticData() {

        prepareDiagnosticData();

        final ValidationContext validationContext = new SignatureValidationContext(validationCertPool);

        final List<AdvancedSignature> allSignatureList = getAllSignatures();

        // The list of all signing certificates is created to allow a parallel validation.
        prepareCertificatesAndTimestamps(allSignatureList, validationContext);

        final ListCRLSource signatureCRLSource = getSignatureCrlSource(allSignatureList);
        certificateVerifier.setSignatureCRLSource(signatureCRLSource);

        final ListOCSPSource signatureOCSPSource = getSignatureOcspSource(allSignatureList);
        certificateVerifier.setSignatureOCSPSource(signatureOCSPSource);

        validationContext.initialize(certificateVerifier);

        validationContext.setCurrentTime(provideProcessExecutorInstance().getCurrentTime());
        validationContext.validate();

        // For each validated signature present in the document to be validated the extraction of diagnostic data is launched.
        final Set<DigestAlgorithm> usedCertificatesDigestAlgorithms = new HashSet<DigestAlgorithm>();
        for (final AdvancedSignature signature : allSignatureList) {

            final XmlSignature xmlSignature = validateSignature(signature);
            usedCertificatesDigestAlgorithms.addAll(signature.getUsedCertificatesDigestAlgorithms());
            jaxbDiagnosticData.getSignature().add(xmlSignature);
        }
        final Set<CertificateToken> processedCertificates = validationContext.getProcessedCertificates();
        dealUsedCertificates(usedCertificatesDigestAlgorithms, processedCertificates);

        return jaxbDiagnosticData;
    }

    /**
     * This method prepares the {@code DiagnosticData} object to store all static information about the signatures being validated.
     */
    private void prepareDiagnosticData() {

        jaxbDiagnosticData = DIAGNOSTIC_DATA_OBJECT_FACTORY.createDiagnosticData();

        String absolutePath = document.getAbsolutePath();
        String documentName = document.getName();
        if (StringUtils.isNotEmpty(absolutePath)) {
            jaxbDiagnosticData.setDocumentName(removeSpecialCharsForXml(absolutePath));
        } else if (StringUtils.isNotEmpty(documentName)) {
            jaxbDiagnosticData.setDocumentName(removeSpecialCharsForXml(documentName));
        } else {
            jaxbDiagnosticData.setDocumentName("?");
        }
    }

    /**
     * Escape special characters which cause problems with jaxb or documentbuilderfactory and namespace aware mode
     */
    private String removeSpecialCharsForXml(String text) {
        return text.replaceAll("&", "");
    }

    /**
     * This method returns the list of all signatures including the countersignatures.
     *
     * @return {@code List} of {@code AdvancedSignature} to validate
     */
    private List<AdvancedSignature> getAllSignatures() {

        final List<AdvancedSignature> allSignatureList = new ArrayList<AdvancedSignature>();
        for (final AdvancedSignature signature : getSignatures()) {

            allSignatureList.add(signature);
            final List<AdvancedSignature> counterSignatures = signature.getCounterSignatures();
            allSignatureList.addAll(counterSignatures);
        }
        return allSignatureList;
    }

    /**
     * For all signatures to be validated this method merges the OCSP sources.
     *
     * @param allSignatureList
     *            {@code List} of {@code AdvancedSignature}s to validate including the countersignatures
     * @return {@code ListCRLSource}
     */
    private ListCRLSource getSignatureCrlSource(final List<AdvancedSignature> allSignatureList) {

        final ListCRLSource signatureCrlSource = new ListCRLSource();
        for (final AdvancedSignature signature : allSignatureList) {

            signatureCrlSource.addAll(signature.getCRLSource());
        }
        return signatureCrlSource;
    }

    /**
     * For all signatures to be validated this method merges the OCSP sources.
     *
     * @param allSignatureList
     *            {@code List} of {@code AdvancedSignature}s to validate including the countersignatures
     * @return {@code ListOCSPSource}
     */
    private ListOCSPSource getSignatureOcspSource(final List<AdvancedSignature> allSignatureList) {

        final ListOCSPSource signatureOcspSource = new ListOCSPSource();
        for (final AdvancedSignature signature : allSignatureList) {

            signatureOcspSource.addAll(signature.getOCSPSource());
        }
        return signatureOcspSource;
    }

    /**
     * @param allSignatureList
     *            {@code List} of {@code AdvancedSignature}s to validate including the countersignatures
     * @param validationContext
     *            {@code ValidationContext} is the implementation of the validators for: certificates, timestamps and revocation data.
     */
    private void prepareCertificatesAndTimestamps(final List<AdvancedSignature> allSignatureList,
            final ValidationContext validationContext) {

        for (final AdvancedSignature signature : allSignatureList) {

            final List<CertificateToken> candidates = signature.getCertificateSource().getCertificates();
            for (final CertificateToken certificateToken : candidates) {
                validationContext.addCertificateTokenForVerification(certificateToken);
            }
            signature.prepareTimestamps(validationContext);
        }
    }

    /**
     * Main method for validating a signature. The diagnostic data is extracted.
     *
     * @param signature
     *            Signature to be validated (can be XAdES, CAdES, PAdES).
     * @return The JAXB object containing all diagnostic data pertaining to the signature
     */
    private XmlSignature validateSignature(final AdvancedSignature signature) throws DSSException {

        final XmlSignature xmlSignature = DIAGNOSTIC_DATA_OBJECT_FACTORY.createXmlSignature();
        try {

            final CertificateToken signingToken = dealSignature(signature, xmlSignature);

            dealPolicy(signature, xmlSignature);

            dealCertificateChain(xmlSignature, signingToken);

            signature.validateTimestamps();

            XmlTimestamps xmlTimestamps = null;
            xmlTimestamps = dealTimestamps(xmlTimestamps, signature.getContentTimestamps());

            xmlTimestamps = dealTimestamps(xmlTimestamps, signature.getSignatureTimestamps());

            xmlTimestamps = dealTimestamps(xmlTimestamps, signature.getTimestampsX1());

            xmlTimestamps = dealTimestamps(xmlTimestamps, signature.getTimestampsX2());

            xmlTimestamps = dealTimestamps(xmlTimestamps, signature.getArchiveTimestamps());

            xmlSignature.setTimestamps(xmlTimestamps);
        } catch (Exception e) {

            // Any raised error is just logged and the process continues with the next signature.
            LOG.warn(e.getMessage(), e);
            addErrorMessage(xmlSignature, e);
        }
        return xmlSignature;
    }

    private void addErrorMessage(final XmlSignature xmlSignature, final Exception e) {

        addErrorMessage(xmlSignature, e.toString());
    }

    private void addErrorMessage(final XmlSignature xmlSignature, final String message) {

        String currentMessage = message;
        String errorMessage = xmlSignature.getErrorMessage();
        if (StringUtils.isBlank(errorMessage)) {
            errorMessage = currentMessage;
        } else {
            errorMessage += "<br />" + currentMessage;
        }
        xmlSignature.setErrorMessage(errorMessage);
    }

    /**
     * @param xmlTimestamps
     * @param timestampTokens
     */
    private XmlTimestamps dealTimestamps(XmlTimestamps xmlTimestamps, final List<TimestampToken> timestampTokens) {

        if (!timestampTokens.isEmpty()) {

            for (final TimestampToken timestampToken : timestampTokens) {

                final XmlTimestampType xmlTimestampToken = xmlForTimestamp(timestampToken);
                if (xmlTimestamps == null) {

                    xmlTimestamps = DIAGNOSTIC_DATA_OBJECT_FACTORY.createXmlTimestamps();
                }
                xmlTimestamps.getTimestamp().add(xmlTimestampToken);
            }
        }
        return xmlTimestamps;
    }

    /**
     * @param timestampToken
     * @return
     */
    private XmlTimestampType xmlForTimestamp(final TimestampToken timestampToken) {

        final XmlTimestampType xmlTimestampToken = DIAGNOSTIC_DATA_OBJECT_FACTORY.createXmlTimestampType();
        xmlTimestampToken.setId(timestampToken.getDSSId().asXmlId());
        final TimestampType timestampType = timestampToken.getTimeStampType();
        xmlTimestampToken.setType(timestampType.name());
        xmlTimestampToken
                .setProductionTime(DSSXMLUtils.createXMLGregorianCalendar(timestampToken.getGenerationTime()));

        xmlTimestampToken.setSignedDataDigestAlgo(timestampToken.getSignedDataDigestAlgo().getName());
        xmlTimestampToken.setEncodedSignedDataDigestValue(timestampToken.getEncodedSignedDataDigestValue());
        xmlTimestampToken.setMessageImprintDataFound(timestampToken.isMessageImprintDataFound());
        xmlTimestampToken.setMessageImprintDataIntact(timestampToken.isMessageImprintDataIntact());
        xmlTimestampToken.setCanonicalizationMethod(timestampToken.getCanonicalizationMethod());

        final SignatureAlgorithm signatureAlgorithm = timestampToken.getSignatureAlgorithm();
        final XmlBasicSignatureType xmlBasicSignatureType = DIAGNOSTIC_DATA_OBJECT_FACTORY
                .createXmlBasicSignatureType();
        if (signatureAlgorithm != null) {

            xmlBasicSignatureType
                    .setEncryptionAlgoUsedToSignThisToken(signatureAlgorithm.getEncryptionAlgorithm().getName());
            xmlBasicSignatureType
                    .setDigestAlgoUsedToSignThisToken(signatureAlgorithm.getDigestAlgorithm().getName());
        }
        final String keyLength = DSSPKUtils.getPublicKeySize(timestampToken);
        xmlBasicSignatureType.setKeyLengthUsedToSignThisToken(keyLength);

        final boolean signatureValid = timestampToken.isSignatureValid();
        xmlBasicSignatureType.setReferenceDataFound(signatureValid /*timestampToken.isReferenceDataFound()*/);
        xmlBasicSignatureType.setReferenceDataIntact(signatureValid /*timestampToken.isReferenceDataIntact()*/);
        xmlBasicSignatureType.setSignatureIntact(signatureValid /*timestampToken.isSignatureIntact()*/);
        xmlBasicSignatureType.setSignatureValid(signatureValid);
        xmlTimestampToken.setBasicSignature(xmlBasicSignatureType);

        final CertificateToken issuerToken = timestampToken.getIssuerToken();

        XmlSigningCertificateType xmlTSSignCert = xmlForSigningCertificate(issuerToken);
        xmlTimestampToken.setSigningCertificate(xmlTSSignCert);

        final XmlCertificateChainType xmlCertChainType = xmlForCertificateChain(issuerToken);
        xmlTimestampToken.setCertificateChain(xmlCertChainType);

        final List<TimestampReference> timestampReferences = timestampToken.getTimestampedReferences();
        if ((timestampReferences != null) && !timestampReferences.isEmpty()) {

            final XmlSignedObjectsType xmlSignedObjectsType = DIAGNOSTIC_DATA_OBJECT_FACTORY
                    .createXmlSignedObjectsType();
            final List<XmlDigestAlgAndValueType> xmlDigestAlgAndValueList = xmlSignedObjectsType
                    .getDigestAlgAndValue();

            for (final TimestampReference timestampReference : timestampReferences) {

                final TimestampReferenceCategory timestampedCategory = timestampReference.getCategory();
                if (TimestampReferenceCategory.SIGNATURE.equals(timestampedCategory)) {

                    final XmlSignedSignature xmlSignedSignature = DIAGNOSTIC_DATA_OBJECT_FACTORY
                            .createXmlSignedSignature();
                    xmlSignedSignature.setId(timestampReference.getSignatureId());
                    xmlSignedObjectsType.getSignedSignature().add(xmlSignedSignature);
                } else {

                    final XmlDigestAlgAndValueType xmlDigestAlgAndValue = DIAGNOSTIC_DATA_OBJECT_FACTORY
                            .createXmlDigestAlgAndValueType();
                    xmlDigestAlgAndValue.setDigestMethod(timestampReference.getDigestAlgorithm());
                    xmlDigestAlgAndValue.setDigestValue(timestampReference.getDigestValue());
                    xmlDigestAlgAndValue.setCategory(timestampedCategory.name());
                    xmlDigestAlgAndValueList.add(xmlDigestAlgAndValue);
                }
            }
            xmlTimestampToken.setSignedObjects(xmlSignedObjectsType);
        }
        return xmlTimestampToken;
    }

    /**
     * @param issuerToken
     * @return
     */
    private XmlCertificateChainType xmlForCertificateChain(final CertificateToken issuerToken) {

        if (issuerToken != null) {

            CertificateToken issuerToken_ = issuerToken;
            final XmlCertificateChainType xmlCertChainType = DIAGNOSTIC_DATA_OBJECT_FACTORY
                    .createXmlCertificateChainType();
            final List<XmlChainCertificate> certChainTokens = xmlCertChainType.getChainCertificate();
            do {

                final XmlChainCertificate xmlCertToken = DIAGNOSTIC_DATA_OBJECT_FACTORY.createXmlChainCertificate();
                xmlCertToken.setId(issuerToken_.getDSSId().asXmlId());
                final CertificateSourceType mainSource = getCertificateMainSourceType(issuerToken_);
                xmlCertToken.setSource(mainSource.name());
                certChainTokens.add(xmlCertToken);
                if (issuerToken_.isTrusted() || issuerToken_.isSelfSigned()) {

                    break;
                }
                issuerToken_ = issuerToken_.getIssuerToken();
            } while (issuerToken_ != null);
            return xmlCertChainType;
        }
        return null;
    }

    private CertificateSourceType getCertificateMainSourceType(final CertificateToken issuerToken) {
        CertificateSourceType mainSource = CertificateSourceType.UNKNOWN;
        final Set<CertificateSourceType> sourceList = issuerToken.getSources();
        if (sourceList.size() > 0) {
            if (sourceList.contains(CertificateSourceType.TRUSTED_LIST)) {
                mainSource = CertificateSourceType.TRUSTED_LIST;
            } else if (sourceList.contains(CertificateSourceType.TRUSTED_STORE)) {
                mainSource = CertificateSourceType.TRUSTED_STORE;
            } else {
                mainSource = sourceList.iterator().next();
            }
        }
        return mainSource;
    }

    /**
     * @param usedCertificatesDigestAlgorithms
     * @param usedCertTokens
     */
    private void dealUsedCertificates(final Set<DigestAlgorithm> usedCertificatesDigestAlgorithms,
            final Set<CertificateToken> usedCertTokens) {

        final XmlUsedCertificates xmlUsedCerts = DIAGNOSTIC_DATA_OBJECT_FACTORY.createXmlUsedCertificates();
        jaxbDiagnosticData.setUsedCertificates(xmlUsedCerts);
        for (final CertificateToken certToken : usedCertTokens) {

            final XmlCertificate xmlCert = dealCertificateDetails(usedCertificatesDigestAlgorithms, certToken);
            // !!! Log the certificate
            if (LOG.isTraceEnabled()) {
                LOG.trace("PEM for certificate: " + certToken.getAbbreviation() + "--->");
                final String pem = DSSUtils.convertToPEM(certToken);
                LOG.trace("\n" + pem);
            }
            dealQCStatement(certToken, xmlCert);
            dealTrustedService(certToken, xmlCert);
            dealRevocationData(certToken, xmlCert);
            dealCertificateValidationInfo(certToken, xmlCert);
            xmlUsedCerts.getCertificate().add(xmlCert);
        }
    }

    /**
     * This method deals with the Qualified Certificate Statements. The retrieved information is transformed to the JAXB object.<br>
     * Qualified Certificate Statements, the following Policies are checked:<br>
     * - Qualified Certificates Policy "0.4.0.1456.1.1? (QCP);<br>
     * - Qualified Certificates Policy "0.4.0.1456.1.2" (QCP+);<br>
     * - Qualified Certificates Compliance "0.4.0.1862.1.1";<br>
     * - Qualified Certificates SCCD "0.4.0.1862.1.4";<br>
     *
     * @param certToken
     * @param xmlCert
     */
    private void dealQCStatement(final CertificateToken certToken, final XmlCertificate xmlCert) {

        if (!certToken.isTrusted()) {

            /// System.out.println("--> QCStatement for: " + certToken.getAbbreviation());
            final XmlQCStatement xmlQCS = DIAGNOSTIC_DATA_OBJECT_FACTORY.createXmlQCStatement();
            xmlQCS.setQCP(qcp.check(certToken));
            xmlQCS.setQCPPlus(qcpPlus.check(certToken));
            xmlQCS.setQCC(qcCompliance.check(certToken));
            xmlQCS.setQCSSCD(qcsscd.check(certToken));
            xmlCert.setQCStatement(xmlQCS);
        }
    }

    /**
     * This method deals with the certificate validation extra information. The retrieved information is transformed to the JAXB object.
     *
     * @param certToken
     * @param xmlCert
     */
    private void dealCertificateValidationInfo(final CertificateToken certToken, final XmlCertificate xmlCert) {

        final List<String> list = certToken.getValidationInfo();
        if (list.size() > 0) {

            final XmlInfoType xmlInfo = DIAGNOSTIC_DATA_OBJECT_FACTORY.createXmlInfoType();
            for (String message : list) {

                final XmlMessage xmlMessage = DIAGNOSTIC_DATA_OBJECT_FACTORY.createXmlMessage();
                xmlMessage.setId(0);
                xmlMessage.setValue(message);
                xmlInfo.getMessage().add(xmlMessage);
            }
            xmlCert.setInfo(xmlInfo);
        }
    }

    /**
     * This method deals with the certificate's details. The retrieved information is transformed to the JAXB object.
     *
     * @param usedDigestAlgorithms
     *            set of different digest algorithms used to compute certificate digest
     * @param certToken
     *            current certificate token
     * @return
     */
    private XmlCertificate dealCertificateDetails(final Set<DigestAlgorithm> usedDigestAlgorithms,
            final CertificateToken certToken) {

        final XmlCertificate xmlCert = DIAGNOSTIC_DATA_OBJECT_FACTORY.createXmlCertificate();

        xmlCert.setId(certToken.getDSSId().asXmlId());

        XmlDistinguishedName xmlDistinguishedName = xmlForDistinguishedName(X500Principal.CANONICAL,
                certToken.getSubjectX500Principal());
        xmlCert.getSubjectDistinguishedName().add(xmlDistinguishedName);
        xmlDistinguishedName = xmlForDistinguishedName(X500Principal.RFC2253, certToken.getSubjectX500Principal());
        xmlCert.getSubjectDistinguishedName().add(xmlDistinguishedName);

        xmlDistinguishedName = xmlForDistinguishedName(X500Principal.CANONICAL, certToken.getIssuerX500Principal());
        xmlCert.getIssuerDistinguishedName().add(xmlDistinguishedName);
        xmlDistinguishedName = xmlForDistinguishedName(X500Principal.RFC2253, certToken.getIssuerX500Principal());
        xmlCert.getIssuerDistinguishedName().add(xmlDistinguishedName);

        xmlCert.setSerialNumber(certToken.getSerialNumber());

        for (final DigestAlgorithm digestAlgorithm : usedDigestAlgorithms) {

            final XmlDigestAlgAndValueType xmlDigestAlgAndValue = new XmlDigestAlgAndValueType();
            xmlDigestAlgAndValue.setDigestMethod(digestAlgorithm.getName());
            xmlDigestAlgAndValue.setDigestValue(DSSUtils.digest(digestAlgorithm, certToken));
            xmlCert.getDigestAlgAndValue().add(xmlDigestAlgAndValue);
        }
        TokenIdentifier issuerTokenDSSId = certToken.getIssuerTokenDSSId();
        if (issuerTokenDSSId != null) {
            xmlCert.setIssuerCertificate(issuerTokenDSSId.asXmlId());
        }
        xmlCert.setNotAfter(DSSXMLUtils.createXMLGregorianCalendar(certToken.getNotAfter()));
        xmlCert.setNotBefore(DSSXMLUtils.createXMLGregorianCalendar(certToken.getNotBefore()));
        final PublicKey publicKey = certToken.getPublicKey();
        xmlCert.setPublicKeySize(DSSPKUtils.getPublicKeySize(publicKey));
        xmlCert.setPublicKeyEncryptionAlgo(DSSPKUtils.getPublicKeyEncryptionAlgo(publicKey));

        xmlForKeyUsageBits(certToken, xmlCert);

        if (DSSASN1Utils.isOCSPSigning(certToken)) {
            xmlCert.setIdKpOCSPSigning(true);
        }
        if (DSSASN1Utils.hasIdPkixOcspNoCheckExtension(certToken)) {
            xmlCert.setIdPkixOcspNoCheck(true);
        }
        if (DSSASN1Utils.hasExpiredCertOnCRLExtension(certToken)) {
            xmlCert.setExpiredCertOnCRL(true);
        }

        final XmlBasicSignatureType xmlBasicSignatureType = DIAGNOSTIC_DATA_OBJECT_FACTORY
                .createXmlBasicSignatureType();

        final SignatureAlgorithm signatureAlgorithm = certToken.getSignatureAlgorithm();
        xmlBasicSignatureType.setDigestAlgoUsedToSignThisToken(signatureAlgorithm.getDigestAlgorithm().getName());
        xmlBasicSignatureType
                .setEncryptionAlgoUsedToSignThisToken(signatureAlgorithm.getEncryptionAlgorithm().getName());
        final String keyLength = DSSPKUtils.getPublicKeySize(certToken);
        xmlBasicSignatureType.setKeyLengthUsedToSignThisToken(keyLength);
        final boolean signatureIntact = certToken.isSignatureValid();
        xmlBasicSignatureType.setReferenceDataFound(signatureIntact);
        xmlBasicSignatureType.setReferenceDataIntact(signatureIntact);
        xmlBasicSignatureType.setSignatureIntact(signatureIntact);
        xmlBasicSignatureType.setSignatureValid(signatureIntact);
        xmlCert.setBasicSignature(xmlBasicSignatureType);

        final CertificateToken issuerToken = certToken.getIssuerToken();
        final XmlSigningCertificateType xmlSigningCertificate = xmlForSigningCertificate(issuerToken);
        xmlCert.setSigningCertificate(xmlSigningCertificate);

        final XmlCertificateChainType xmlCertChainType = xmlForCertificateChain(issuerToken);
        xmlCert.setCertificateChain(xmlCertChainType);

        xmlCert.setSelfSigned(certToken.isSelfSigned());
        xmlCert.setTrusted(certToken.isTrusted());

        return xmlCert;
    }

    private void xmlForKeyUsageBits(CertificateToken certToken, XmlCertificate xmlCert) {
        final Set<KeyUsageBit> keyUsageBits = certToken.getKeyUsageBits();
        if (CollectionUtils.isEmpty(keyUsageBits)) {
            return;
        }
        final XmlKeyUsageBits xmlKeyUsageBits = DIAGNOSTIC_DATA_OBJECT_FACTORY.createXmlKeyUsageBits();
        final List<String> xmlKeyUsageBitItems = xmlKeyUsageBits.getKeyUsage();
        for (final KeyUsageBit keyUsageBit : keyUsageBits) {
            xmlKeyUsageBitItems.add(keyUsageBit.name());
        }
        xmlCert.setKeyUsageBits(xmlKeyUsageBits);
    }

    private XmlDistinguishedName xmlForDistinguishedName(final String x500PrincipalFormat,
            final X500Principal X500PrincipalName) {

        final XmlDistinguishedName xmlDistinguishedName = DIAGNOSTIC_DATA_OBJECT_FACTORY
                .createXmlDistinguishedName();
        xmlDistinguishedName.setFormat(x500PrincipalFormat);
        final String x500PrincipalName = X500PrincipalName.getName(x500PrincipalFormat);
        xmlDistinguishedName.setValue(x500PrincipalName);
        return xmlDistinguishedName;
    }

    /**
     * This method deals with the certificate chain. The retrieved information is transformed to the JAXB object.
     *
     * @param xmlSignature
     *            The JAXB object containing all diagnostic data pertaining to the signature
     * @param signingToken
     *            {@code CertificateToken} relative to the current signature
     */
    private void dealCertificateChain(final XmlSignature xmlSignature, final CertificateToken signingToken) {

        if (signingToken != null) {

            final XmlCertificateChainType xmlCertChainType = xmlForCertificateChain(signingToken);
            xmlSignature.setCertificateChain(xmlCertChainType);
        }
    }

    /**
     * This method deals with the trusted service information in case of trusted certificate. The retrieved information is transformed to the JAXB object.
     *
     * @param certToken
     * @param xmlCert
     */
    private void dealTrustedService(final CertificateToken certToken, final XmlCertificate xmlCert) {

        if (certToken.isTrusted()) {

            return;
        }
        final CertificateToken trustAnchor = certToken.getTrustAnchor();
        if (trustAnchor == null) {

            return;
        }
        final Set<ServiceInfo> services = trustAnchor.getAssociatedTSPS();
        if (services == null) {

            return;
        }
        for (final ServiceInfo serviceInfo : services) {

            //         System.out.println("---------------------------------------------");
            //         System.out.println(serviceInfo);

            final XmlTrustedServiceProviderType xmlTSP = DIAGNOSTIC_DATA_OBJECT_FACTORY
                    .createXmlTrustedServiceProviderType();
            xmlTSP.setTSPName(serviceInfo.getTspName());
            xmlTSP.setTSPServiceName(serviceInfo.getServiceName());
            xmlTSP.setTSPServiceType(serviceInfo.getType());
            xmlTSP.setWellSigned(serviceInfo.isTlWellSigned());

            final Date statusStartDate = serviceInfo.getStatusStartDate();
            xmlTSP.setStatus(serviceInfo.getStatus());
            xmlTSP.setStartDate(DSSXMLUtils.createXMLGregorianCalendar(statusStartDate));
            xmlTSP.setEndDate(DSSXMLUtils.createXMLGregorianCalendar(serviceInfo.getStatusEndDate()));
            xmlTSP.setExpiredCertsRevocationInfo(
                    DSSXMLUtils.createXMLGregorianCalendar(serviceInfo.getExpiredCertsRevocationInfo()));

            // Check of the associated conditions to identify the qualifiers
            final List<String> qualifiers = getQualifiers(serviceInfo, certToken);
            if (!qualifiers.isEmpty()) {

                final XmlQualifiers xmlQualifiers = DIAGNOSTIC_DATA_OBJECT_FACTORY.createXmlQualifiers();
                for (String qualifier : qualifiers) {

                    xmlQualifiers.getQualifier().add(qualifier);
                }
                xmlTSP.setQualifiers(xmlQualifiers);
            }
            xmlCert.getTrustedServiceProvider().add(xmlTSP);
        }
    }

    /**
     * Retrieves all the qualifiers for which the corresponding conditionEntry is true.
     *
     * @param certificateToken
     * @return
     */
    public List<String> getQualifiers(ServiceInfo serviceInfo, CertificateToken certificateToken) {

        LOG.trace("--> GET_QUALIFIERS()");
        List<String> list = new ArrayList<String>();
        Map<String, List<Condition>> qualifiersAndConditions = serviceInfo.getQualifiersAndConditions();
        for (Entry<String, List<Condition>> conditionEntry : qualifiersAndConditions.entrySet()) {
            List<Condition> conditions = conditionEntry.getValue();
            LOG.trace("  --> " + conditions);
            for (final Condition condition : conditions) {
                if (condition.check(certificateToken)) {
                    LOG.trace("    --> CONDITION TRUE / " + conditionEntry.getKey());
                    list.add(conditionEntry.getKey());
                    break;
                }
            }
        }
        return list;
    }

    /**
     * This method deals with the revocation data of a certificate. The retrieved information is transformed to the JAXB object.
     *
     * @param certToken
     * @param xmlCert
     */
    private void dealRevocationData(final CertificateToken certToken, final XmlCertificate xmlCert) {

        final XmlRevocationType xmlRevocation = DIAGNOSTIC_DATA_OBJECT_FACTORY.createXmlRevocationType();
        final RevocationToken revocationToken = certToken.getRevocationToken();
        if (revocationToken != null) {

            final Boolean revocationTokenStatus = revocationToken.getStatus();
            // revocationTokenStatus can be null when OCSP return Unknown. In this case we set status to false.
            xmlRevocation.setStatus(revocationTokenStatus == null ? false : revocationTokenStatus);
            xmlRevocation.setDateTime(DSSXMLUtils.createXMLGregorianCalendar(revocationToken.getRevocationDate()));
            xmlRevocation.setReason(revocationToken.getReason());
            xmlRevocation.setIssuingTime(DSSXMLUtils.createXMLGregorianCalendar(revocationToken.getIssuingTime()));
            xmlRevocation.setNextUpdate(DSSXMLUtils.createXMLGregorianCalendar(revocationToken.getNextUpdate()));
            xmlRevocation.setSource(revocationToken.getClass().getSimpleName());
            xmlRevocation.setSourceAddress(revocationToken.getSourceURL());

            final XmlBasicSignatureType xmlBasicSignatureType = DIAGNOSTIC_DATA_OBJECT_FACTORY
                    .createXmlBasicSignatureType();
            final SignatureAlgorithm revocationSignatureAlgo = revocationToken.getSignatureAlgorithm();
            final boolean unknownAlgorithm = revocationSignatureAlgo == null;
            final String encryptionAlgorithmName = unknownAlgorithm ? "?"
                    : revocationSignatureAlgo.getEncryptionAlgorithm().getName();
            xmlBasicSignatureType.setEncryptionAlgoUsedToSignThisToken(encryptionAlgorithmName);
            final String keyLength = DSSPKUtils.getPublicKeySize(revocationToken);
            xmlBasicSignatureType.setKeyLengthUsedToSignThisToken(keyLength);

            final String digestAlgorithmName = unknownAlgorithm ? "?"
                    : revocationSignatureAlgo.getDigestAlgorithm().getName();
            xmlBasicSignatureType.setDigestAlgoUsedToSignThisToken(digestAlgorithmName);
            final boolean signatureValid = revocationToken.isSignatureValid();
            xmlBasicSignatureType.setReferenceDataFound(signatureValid);
            xmlBasicSignatureType.setReferenceDataIntact(signatureValid);
            xmlBasicSignatureType.setSignatureIntact(signatureValid);
            xmlBasicSignatureType.setSignatureValid(signatureValid);
            xmlRevocation.setBasicSignature(xmlBasicSignatureType);

            final CertificateToken issuerToken = revocationToken.getIssuerToken();
            final XmlSigningCertificateType xmlRevocationSignCert = xmlForSigningCertificate(issuerToken);
            xmlRevocation.setSigningCertificate(xmlRevocationSignCert);

            final XmlCertificateChainType xmlCertChainType = xmlForCertificateChain(issuerToken);
            xmlRevocation.setCertificateChain(xmlCertChainType);

            final List<String> list = revocationToken.getValidationInfo();
            if (list.size() > 0) {

                final XmlInfoType xmlInfo = DIAGNOSTIC_DATA_OBJECT_FACTORY.createXmlInfoType();
                for (String message : list) {

                    final XmlMessage xmlMessage = DIAGNOSTIC_DATA_OBJECT_FACTORY.createXmlMessage();
                    xmlMessage.setId(0);
                    xmlMessage.setValue(message);
                    xmlInfo.getMessage().add(xmlMessage);
                }
                xmlRevocation.setInfo(xmlInfo);
            }
            xmlCert.setRevocation(xmlRevocation);
        }
    }

    /**
     * This method deals with the signature policy. The retrieved information is transformed to the JAXB object.
     *
     * @param signature
     *            Signature to be validated (can be XAdES, CAdES, PAdES).
     * @param xmlSignature
     *            The JAXB object containing all diagnostic data pertaining to the signature
     */
    private void dealPolicy(final AdvancedSignature signature, final XmlSignature xmlSignature) {

        SignaturePolicy signaturePolicy = null;
        try {
            signaturePolicy = signature.getPolicyId();
        } catch (Exception e) {
            final String msg = "Error when extracting the signature policy: " + e.getMessage();
            LOG.warn(msg, e);
            addErrorMessage(xmlSignature, msg);
        }
        if (signaturePolicy == null) {
            return;
        }

        final XmlPolicy xmlPolicy = DIAGNOSTIC_DATA_OBJECT_FACTORY.createXmlPolicy();
        xmlSignature.setPolicy(xmlPolicy);

        final String policyId = signaturePolicy.getIdentifier();
        xmlPolicy.setId(policyId);

        final String policyUrl = signaturePolicy.getUrl();
        xmlPolicy.setUrl(policyUrl);

        final String notice = signaturePolicy.getNotice();
        xmlPolicy.setNotice(notice);

        final String policyDigestValueFromSignature = StringUtils.upperCase(signaturePolicy.getDigestValue());

        final DigestAlgorithm signPolicyHashAlgFromSignature = signaturePolicy.getDigestAlgorithm();

        XmlDigestAlgAndValueType xmlDigestAlgAndValue = DIAGNOSTIC_DATA_OBJECT_FACTORY
                .createXmlDigestAlgAndValueType();
        xmlDigestAlgAndValue.setDigestMethod(
                signPolicyHashAlgFromSignature == null ? "" : signPolicyHashAlgFromSignature.getName());
        xmlDigestAlgAndValue.setDigestValue(policyDigestValueFromSignature);
        xmlPolicy.setDigestAlgAndValue(xmlDigestAlgAndValue);

        /**
         * ETSI 102 853:
         * 3) Obtain the digest of the resulting document against which the digest value present in the property/attribute will be checked:
         */
        if ((policyDocument == null) && StringUtils.isEmpty(policyUrl)) {
            xmlPolicy.setIdentified(false);
            if (policyId.isEmpty()) {
                xmlPolicy.setStatus(true);
            } else {
                xmlPolicy.setStatus(false);
            }
            return;
        }
        xmlPolicy.setIdentified(true);

        byte[] policyBytes = null;
        try {
            if (policyDocument == null) {
                final DataLoader dataLoader = certificateVerifier.getDataLoader();
                policyBytes = dataLoader.get(policyUrl);
            } else {
                policyBytes = DSSUtils.toByteArray(policyDocument);
            }
        } catch (Exception e) {
            // When any error (communication) we just set the status to false
            xmlPolicy.setStatus(false);
            xmlPolicy.setProcessingError(e.getMessage());
            //Do nothing
            LOG.warn(e.getMessage(), e);
            return;
        }

        boolean isAsn1Processable;
        ASN1Sequence asn1Sequence = null;
        try {
            asn1Sequence = DSSASN1Utils.toASN1Primitive(policyBytes);
            isAsn1Processable = true;
        } catch (Exception e) {
            LOG.info("Policy bytes are not asn1 processable : " + e.getMessage());
            isAsn1Processable = false;
        }
        xmlPolicy.setAsn1Processable(isAsn1Processable);

        try {

            if (isAsn1Processable) {
                /**
                 * a)
                 * If the resulting document is based on TR 102 272 [i.2] (ESI: ASN.1 format for signature policies), use the digest value present in the
                 * SignPolicyDigest element from the resulting document. Check that the digest algorithm indicated
                 * in the SignPolicyDigestAlg from the resulting document is equal to the digest algorithm indicated in the property.
                 */

                final ASN1Sequence signPolicyHashAlgObject = (ASN1Sequence) asn1Sequence.getObjectAt(0);
                final AlgorithmIdentifier signPolicyHashAlgIdentifier = AlgorithmIdentifier
                        .getInstance(signPolicyHashAlgObject);
                DigestAlgorithm signPolicyHashAlgFromPolicy = DigestAlgorithm
                        .forOID(signPolicyHashAlgIdentifier.getAlgorithm().getId());

                /**
                 * b)
                 * If the resulting document is based on TR 102 038 [i.3] ((ESI) XML format for signature policies), use the digest value present in
                 * signPolicyHash element from the resulting document. Check that the digest
                 * algorithm indicated in the signPolicyHashAlg from the resulting document is equal to the digest algorithm indicated in the attribute.
                 */

                /**
                 * The use of a zero-sigPolicyHash value is to ensure backwards compatibility with earlier versions of the
                 * current document. If sigPolicyHash is zero, then the hash value should not be checked against the
                 * calculated hash value of the signature policy.
                 */
                if (!signPolicyHashAlgFromPolicy.equals(signPolicyHashAlgFromSignature)) {
                    xmlPolicy.setProcessingError(
                            "The digest algorithm indicated in the SignPolicyHashAlg from the resulting document ("
                                    + signPolicyHashAlgFromPolicy + ") is not equal to the digest " + "algorithm ("
                                    + signPolicyHashAlgFromSignature + ").");
                    xmlPolicy.setDigestAlgorithmsEqual(false);
                    xmlPolicy.setStatus(false);
                    return;
                } else {
                    xmlPolicy.setDigestAlgorithmsEqual(true);
                }

                byte[] recalculatedDigestValue = DSSASN1Utils
                        .getAsn1SignaturePolicyDigest(signPolicyHashAlgFromPolicy, policyBytes);
                String recalculatedDigestHexValue = DSSUtils.toHex(recalculatedDigestValue);

                boolean equal = policyDigestValueFromSignature.equals(recalculatedDigestHexValue);
                xmlPolicy.setStatus(equal);
                if (!equal) {
                    xmlPolicy.setProcessingError("The policy digest value (" + policyDigestValueFromSignature
                            + ") does not match the re-calculated digest value (" + recalculatedDigestHexValue
                            + ").");
                    return;
                }

                final ASN1OctetString signPolicyHash = (ASN1OctetString) asn1Sequence.getObjectAt(2);
                final byte[] policyDigestValueFromPolicy = signPolicyHash.getOctets();
                String policyDigestHexValueFromPolicy = DSSUtils.toHex(policyDigestValueFromPolicy);
                equal = policyDigestValueFromSignature.equals(policyDigestHexValueFromPolicy);
                xmlPolicy.setStatus(equal);
                if (!equal) {
                    xmlPolicy.setProcessingError("The policy digest value (" + policyDigestValueFromSignature
                            + ") does not match the digest value from the policy file ("
                            + policyDigestHexValueFromPolicy + ").");
                }
            } else {

                /**
                 * c)
                 * In all other cases, compute the digest using the digesting algorithm indicated in the children of the property/attribute.
                 */

                byte[] recalculatedDigestValue = DSSUtils.digest(signPolicyHashAlgFromSignature, policyBytes);
                String recalculatedDigestHexValue = DSSUtils.toHex(recalculatedDigestValue);

                boolean equal = policyDigestValueFromSignature.equals(recalculatedDigestHexValue);
                xmlPolicy.setStatus(equal);
                if (!equal) {
                    xmlPolicy.setProcessingError("The policy digest value (" + policyDigestValueFromSignature
                            + ") does not match the re-calculated digest value (" + recalculatedDigestHexValue
                            + ").");
                    return;
                }
            }

        } catch (RuntimeException e) {
            // When any error (communication) we just set the status to false
            xmlPolicy.setStatus(false);
            xmlPolicy.setProcessingError(e.getMessage());
            //Do nothing
            LOG.warn(e.getMessage(), e);
        }
    }

    /**
     * This method deals with the basic signature data. The retrieved information is transformed to the JAXB object. The signing certificate token is returned if found.
     *
     * @param signature
     *            Signature to be validated (can be XAdES, CAdES, PAdES).
     * @param xmlSignature
     *            The JAXB object containing all diagnostic data pertaining to the signature
     * @return
     */
    private CertificateToken dealSignature(final AdvancedSignature signature, final XmlSignature xmlSignature) {

        final AdvancedSignature masterSignature = signature.getMasterSignature();
        if (masterSignature != null) {

            xmlSignature.setType(AttributeValue.COUNTERSIGNATURE);
            xmlSignature.setParentId(masterSignature.getId());
        }
        performStructuralValidation(signature, xmlSignature);
        performSignatureCryptographicValidation(signature, xmlSignature);
        xmlSignature.setId(signature.getId());
        xmlSignature.setDateTime(DSSXMLUtils.createXMLGregorianCalendar(signature.getSigningTime()));
        final SignatureLevel dataFoundUpToLevel = signature.getDataFoundUpToLevel();
        final String value = dataFoundUpToLevel == null ? "UNKNOWN" : dataFoundUpToLevel.name();
        xmlSignature.setSignatureFormat(value);

        dealWithSignatureProductionPlace(signature, xmlSignature);

        dealWithCommitmentTypeIndication(signature, xmlSignature);

        dealWithClaimedRole(signature, xmlSignature);

        final String contentType = signature.getContentType();
        xmlSignature.setContentType(contentType);

        final String contentIdentifier = signature.getContentIdentifier();
        xmlSignature.setContentIdentifier(contentIdentifier);

        final String contentHints = signature.getContentHints();
        xmlSignature.setContentHints(contentHints);

        dealWithCertifiedRole(signature, xmlSignature);

        final CertificateValidity certificateValidity = dealSigningCertificate(signature, xmlSignature);

        final XmlBasicSignatureType xmlBasicSignature = getXmlBasicSignatureType(xmlSignature);
        final EncryptionAlgorithm encryptionAlgorithm = signature.getEncryptionAlgorithm();
        final String encryptionAlgorithmString = encryptionAlgorithm == null ? "?" : encryptionAlgorithm.getName();
        xmlBasicSignature.setEncryptionAlgoUsedToSignThisToken(encryptionAlgorithmString);
        // signingCertificateValidity can be null in case of a non AdES signature.
        final CertificateToken signingCertificateToken = certificateValidity == null ? null
                : certificateValidity.getCertificateToken();
        final int keyLength = signingCertificateToken == null ? 0
                : DSSPKUtils.getPublicKeySize(signingCertificateToken.getPublicKey());
        xmlBasicSignature.setKeyLengthUsedToSignThisToken(String.valueOf(keyLength));
        final DigestAlgorithm digestAlgorithm = signature.getDigestAlgorithm();
        final String digestAlgorithmString = digestAlgorithm == null ? "?" : digestAlgorithm.getName();
        xmlBasicSignature.setDigestAlgoUsedToSignThisToken(digestAlgorithmString);
        xmlSignature.setBasicSignature(xmlBasicSignature);
        dealSignatureScope(xmlSignature, signature);

        return signingCertificateToken;
    }

    private void performStructuralValidation(final AdvancedSignature signature, final XmlSignature xmlSignature) {

        final ValidationPolicy validationPolicy = processExecutor.getValidationPolicy();
        if ((validationPolicy == null) || (validationPolicy.getStructuralValidationConstraint() == null)) {
            return;
        }
        final String structureValid = signature.validateStructure();
        if (structureValid != null) {

            final XmlStructuralValidationType xmlStructuralValidationType = DIAGNOSTIC_DATA_OBJECT_FACTORY
                    .createXmlStructuralValidationType();
            xmlStructuralValidationType.setValid(StringUtils.EMPTY.equals(structureValid));
            if (!StringUtils.EMPTY.equals(structureValid)) {
                xmlStructuralValidationType.setMessage(structureValid);
            }
            xmlSignature.setStructuralValidation(xmlStructuralValidationType);
        }
    }

    private void dealWithSignatureProductionPlace(AdvancedSignature signature, XmlSignature xmlSignature) {
        final SignatureProductionPlace signatureProductionPlace = signature.getSignatureProductionPlace();
        if (signatureProductionPlace != null) {

            final XmlSignatureProductionPlace xmlSignatureProductionPlace = DIAGNOSTIC_DATA_OBJECT_FACTORY
                    .createXmlSignatureProductionPlace();
            xmlSignatureProductionPlace.setCountryName(signatureProductionPlace.getCountryName());
            xmlSignatureProductionPlace.setStateOrProvince(signatureProductionPlace.getStateOrProvince());
            xmlSignatureProductionPlace.setPostalCode(signatureProductionPlace.getPostalCode());
            xmlSignatureProductionPlace.setAddress(signatureProductionPlace.getStreetAddress());
            xmlSignatureProductionPlace.setCity(signatureProductionPlace.getCity());
            xmlSignature.setSignatureProductionPlace(xmlSignatureProductionPlace);
        }
    }

    private void dealWithCertifiedRole(AdvancedSignature signature, XmlSignature xmlSignature) {
        List<CertifiedRole> certifiedRoles = null;
        try {
            certifiedRoles = signature.getCertifiedSignerRoles();
        } catch (DSSException e) {

            LOG.warn("Exception", e);
            addErrorMessage(xmlSignature, e);
        }
        if ((certifiedRoles != null) && !certifiedRoles.isEmpty()) {

            for (final CertifiedRole certifiedRole : certifiedRoles) {

                final XmlCertifiedRolesType xmlCertifiedRolesType = DIAGNOSTIC_DATA_OBJECT_FACTORY
                        .createXmlCertifiedRolesType();

                xmlCertifiedRolesType.setCertifiedRole(certifiedRole.getRole());
                xmlCertifiedRolesType
                        .setNotBefore(DSSXMLUtils.createXMLGregorianCalendar(certifiedRole.getNotBefore()));
                xmlCertifiedRolesType
                        .setNotAfter(DSSXMLUtils.createXMLGregorianCalendar(certifiedRole.getNotAfter()));
                xmlSignature.getCertifiedRoles().add(xmlCertifiedRolesType);
            }
        }
    }

    private void dealWithClaimedRole(AdvancedSignature signature, XmlSignature xmlSignature) {
        String[] claimedRoles = null;
        try {
            claimedRoles = signature.getClaimedSignerRoles();
        } catch (DSSException e) {

            LOG.warn("Exception: ", e);
            addErrorMessage(xmlSignature, e);
        }
        if ((claimedRoles != null) && (claimedRoles.length > 0)) {

            final XmlClaimedRoles xmlClaimedRoles = DIAGNOSTIC_DATA_OBJECT_FACTORY.createXmlClaimedRoles();
            for (final String claimedRole : claimedRoles) {

                xmlClaimedRoles.getClaimedRole().add(claimedRole);
            }
            xmlSignature.setClaimedRoles(xmlClaimedRoles);
        }
    }

    private void dealWithCommitmentTypeIndication(AdvancedSignature signature, XmlSignature xmlSignature) {
        CommitmentType commitmentTypeIndication = null;
        try {
            commitmentTypeIndication = signature.getCommitmentTypeIndication();
        } catch (Exception e) {

            LOG.warn("Exception: ", e);
            addErrorMessage(xmlSignature, e);
        }
        if (commitmentTypeIndication != null) {

            final XmlCommitmentTypeIndication xmlCommitmentTypeIndication = DIAGNOSTIC_DATA_OBJECT_FACTORY
                    .createXmlCommitmentTypeIndication();
            final List<String> xmlIdentifiers = xmlCommitmentTypeIndication.getIdentifier();

            final List<String> identifiers = commitmentTypeIndication.getIdentifiers();
            for (final String identifier : identifiers) {

                xmlIdentifiers.add(identifier);
            }
            xmlSignature.setCommitmentTypeIndication(xmlCommitmentTypeIndication);
        }
    }

    protected void dealSignatureScope(XmlSignature xmlSignature, AdvancedSignature signature) {
        final XmlSignatureScopes xmlSignatureScopes = new XmlSignatureScopes();
        final List<SignatureScope> signatureScope = signatureScopeFinder.findSignatureScope(signature);
        for (final SignatureScope scope : signatureScope) {
            final XmlSignatureScopeType xmlSignatureScope = new XmlSignatureScopeType();
            xmlSignatureScope.setName(scope.getName());
            xmlSignatureScope.setScope(scope.getType());
            xmlSignatureScope.setValue(scope.getDescription());

            xmlSignatureScopes.getSignatureScope().add(xmlSignatureScope);
        }
        xmlSignature.setSignatureScopes(xmlSignatureScopes);
    }

    private XmlBasicSignatureType getXmlBasicSignatureType(XmlSignature xmlSignature) {
        XmlBasicSignatureType xmlBasicSignature = xmlSignature.getBasicSignature();
        if (xmlBasicSignature == null) {

            xmlBasicSignature = DIAGNOSTIC_DATA_OBJECT_FACTORY.createXmlBasicSignatureType();
        }
        return xmlBasicSignature;
    }

    /**
     * This method verifies the cryptographic integrity of the signature: the references are identified, their digest is checked and then the signature itself. The result of these
     * verifications is transformed to the JAXB representation.
     *
     * @param signature
     *            Signature to be validated (can be XAdES, CAdES, PAdES).
     * @param xmlSignature
     *            The JAXB object containing all diagnostic data pertaining to the signature
     */
    private void performSignatureCryptographicValidation(final AdvancedSignature signature,
            final XmlSignature xmlSignature) {

        final SignatureCryptographicVerification scv = signature.checkSignatureIntegrity();
        final XmlBasicSignatureType xmlBasicSignature = getXmlBasicSignatureType(xmlSignature);
        xmlBasicSignature.setReferenceDataFound(scv.isReferenceDataFound());
        xmlBasicSignature.setReferenceDataIntact(scv.isReferenceDataIntact());
        xmlBasicSignature.setSignatureIntact(scv.isSignatureIntact());
        xmlBasicSignature.setSignatureValid(scv.isSignatureValid());
        xmlSignature.setBasicSignature(xmlBasicSignature);
        if (!scv.getErrorMessage().isEmpty()) {

            xmlSignature.setErrorMessage(scv.getErrorMessage());
        }
    }

    /**
     * This method finds the signing certificate and creates its JAXB object representation. This is the signing certificate used to produce the main signature (signature being
     * analysed). If the signingToken is null (the signing certificate was not found) then Id is set to 0.
     *
     * @param signature
     *            Signature to be validated (can be XAdES, CAdES, PAdES).
     * @param xmlSignature
     *            The JAXB object containing all diagnostic data pertaining to the signature
     * @return
     */
    private CertificateValidity dealSigningCertificate(final AdvancedSignature signature,
            final XmlSignature xmlSignature) {

        final XmlSigningCertificateType xmlSignCertType = DIAGNOSTIC_DATA_OBJECT_FACTORY
                .createXmlSigningCertificateType();
        signature.checkSigningCertificate();
        final CandidatesForSigningCertificate candidatesForSigningCertificate = signature
                .getCandidatesForSigningCertificate();
        final CertificateValidity theCertificateValidity = candidatesForSigningCertificate
                .getTheCertificateValidity();
        if (theCertificateValidity != null) {

            final CertificateToken signingCertificateToken = theCertificateValidity.getCertificateToken();
            if (signingCertificateToken != null) {

                xmlSignCertType.setId(signingCertificateToken.getDSSId().asXmlId());
            }
            xmlSignCertType.setAttributePresent(theCertificateValidity.isAttributePresent());
            xmlSignCertType.setDigestValuePresent(theCertificateValidity.isDigestPresent());
            xmlSignCertType.setDigestValueMatch(theCertificateValidity.isDigestEqual());
            final boolean issuerSerialMatch = theCertificateValidity.isSerialNumberEqual()
                    && theCertificateValidity.isDistinguishedNameEqual();
            xmlSignCertType.setIssuerSerialMatch(issuerSerialMatch);
            xmlSignCertType.setSigned(theCertificateValidity.getSigned());
            xmlSignature.setSigningCertificate(xmlSignCertType);
        }
        return theCertificateValidity;
    }

    /**
     * This method creates the SigningCertificate element for the current token.
     *
     * @param issuerCertificateToken
     *            the issuer certificate of the current token
     * @return
     */
    protected XmlSigningCertificateType xmlForSigningCertificate(final CertificateToken issuerCertificateToken) {

        if (issuerCertificateToken != null) {

            final XmlSigningCertificateType xmlSignCertType = DIAGNOSTIC_DATA_OBJECT_FACTORY
                    .createXmlSigningCertificateType();

            xmlSignCertType.setId(issuerCertificateToken.getDSSId().asXmlId());
            return xmlSignCertType;
        }
        return null;
    }

    /**
     * This method allows to define the sequence of the validator related to a document to validate. It's only used with ASiC-E container.
     *
     * @param validator
     *            {@code SignedDocumentValidator} corresponding to the next signature with in the contained.
     */
    public void setNextValidator(final DocumentValidator validator) {
        throw new DSSUnsupportedOperationException("This method is not applicable in this context!");
    }

    @Override
    public DocumentValidator getNextValidator() {
        return null;
    }

    @Override
    public DocumentValidator getSubordinatedValidator() {
        return null;
    }

}