eu.europa.ec.markt.dss.validation102853.SignedDocumentValidator.java Source code

Java tutorial

Introduction

Here is the source code for eu.europa.ec.markt.dss.validation102853.SignedDocumentValidator.java

Source

/*
 * DSS - Digital Signature Services
 *
 * Copyright (C) 2013 European Commission, Directorate-General Internal Market and Services (DG MARKT), B-1049 Bruxelles/Brussel
 *
 * Developed by: 2013 ARHS Developments S.A. (rue Nicolas Bov 2B, L-1253 Luxembourg) http://www.arhs-developments.com
 *
 * This file is part of the "DSS - Digital Signature Services" project.
 *
 * "DSS - Digital Signature Services" 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.
 *
 * DSS 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
 * "DSS - Digital Signature Services".  If not, see <http://www.gnu.org/licenses/>.
 */

package eu.europa.ec.markt.dss.validation102853;

import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.security.PublicKey;
import java.security.cert.X509Certificate;
import java.util.Arrays;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.logging.Logger;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;

import javax.security.auth.x500.X500Principal;
import javax.xml.datatype.XMLGregorianCalendar;
import javax.xml.parsers.ParserConfigurationException;

import org.apache.commons.io.IOUtils;
import org.bouncycastle.asn1.x509.qualified.ETSIQCObjectIdentifiers;
import org.bouncycastle.cms.CMSException;
import org.w3c.dom.Document;
import org.xml.sax.SAXException;

import eu.europa.ec.markt.dss.DSSUtils;
import eu.europa.ec.markt.dss.DigestAlgorithm;
import eu.europa.ec.markt.dss.OID;
import eu.europa.ec.markt.dss.SignatureAlgorithm;
import eu.europa.ec.markt.dss.exception.DSSException;
import eu.europa.ec.markt.dss.exception.NotETSICompliantException;
import eu.europa.ec.markt.dss.exception.NotETSICompliantException.MSG;
import eu.europa.ec.markt.dss.signature.DSSDocument;
import eu.europa.ec.markt.dss.signature.InMemoryDocument;
import eu.europa.ec.markt.dss.signature.ProfileException;
import eu.europa.ec.markt.dss.validation.PolicyValue;
import eu.europa.ec.markt.dss.validation.certificate.CertificateSourceType;
import eu.europa.ec.markt.dss.validation102853.asic.ASiCXMLDocumentValidator;
import eu.europa.ec.markt.dss.validation102853.bean.SignatureCryptographicVerification;
import eu.europa.ec.markt.dss.validation102853.bean.SignatureProductionPlace;
import eu.europa.ec.markt.dss.validation102853.bean.SigningCertificate;
import eu.europa.ec.markt.dss.validation102853.cades.CMSDocumentValidator;
import eu.europa.ec.markt.dss.validation102853.condition.Condition;
import eu.europa.ec.markt.dss.validation102853.condition.PolicyIdCondition;
import eu.europa.ec.markt.dss.validation102853.condition.QcStatementCondition;
import eu.europa.ec.markt.dss.validation102853.condition.ServiceInfo;
import eu.europa.ec.markt.dss.validation102853.data.diagnostic.DiagnosticData;
import eu.europa.ec.markt.dss.validation102853.data.diagnostic.ObjectFactory;
import eu.europa.ec.markt.dss.validation102853.data.diagnostic.XmlArchiveTimestamps;
import eu.europa.ec.markt.dss.validation102853.data.diagnostic.XmlCertificate;
import eu.europa.ec.markt.dss.validation102853.data.diagnostic.XmlCertificateChainType;
import eu.europa.ec.markt.dss.validation102853.data.diagnostic.XmlChainCertificate;
import eu.europa.ec.markt.dss.validation102853.data.diagnostic.XmlClaimedRoles;
import eu.europa.ec.markt.dss.validation102853.data.diagnostic.XmlContentTimestamps;
import eu.europa.ec.markt.dss.validation102853.data.diagnostic.XmlDigestAlgAndValueType;
import eu.europa.ec.markt.dss.validation102853.data.diagnostic.XmlInfoType;
import eu.europa.ec.markt.dss.validation102853.data.diagnostic.XmlIssuerDistinguishedName;
import eu.europa.ec.markt.dss.validation102853.data.diagnostic.XmlMessage;
import eu.europa.ec.markt.dss.validation102853.data.diagnostic.XmlPolicy;
import eu.europa.ec.markt.dss.validation102853.data.diagnostic.XmlQCStatement;
import eu.europa.ec.markt.dss.validation102853.data.diagnostic.XmlQualifiers;
import eu.europa.ec.markt.dss.validation102853.data.diagnostic.XmlRefsOnlyTimestamps;
import eu.europa.ec.markt.dss.validation102853.data.diagnostic.XmlRevocationType;
import eu.europa.ec.markt.dss.validation102853.data.diagnostic.XmlSigAndRefsTimestamps;
import eu.europa.ec.markt.dss.validation102853.data.diagnostic.XmlSignature;
import eu.europa.ec.markt.dss.validation102853.data.diagnostic.XmlSignatureProductionPlace;
import eu.europa.ec.markt.dss.validation102853.data.diagnostic.XmlSignedObjectsType;
import eu.europa.ec.markt.dss.validation102853.data.diagnostic.XmlSignedSignature;
import eu.europa.ec.markt.dss.validation102853.data.diagnostic.XmlSigningCertificateType;
import eu.europa.ec.markt.dss.validation102853.data.diagnostic.XmlSubjectDistinguishedName;
import eu.europa.ec.markt.dss.validation102853.data.diagnostic.XmlTimestampType;
import eu.europa.ec.markt.dss.validation102853.data.diagnostic.XmlTimestamps;
import eu.europa.ec.markt.dss.validation102853.data.diagnostic.XmlTrustedServiceProviderType;
import eu.europa.ec.markt.dss.validation102853.data.diagnostic.XmlUsedCertificates;
import eu.europa.ec.markt.dss.validation102853.pades.PDFDocumentValidator;
import eu.europa.ec.markt.dss.validation102853.report.SimpleReport;
import eu.europa.ec.markt.dss.validation102853.report.ValidationReport;
import eu.europa.ec.markt.dss.validation102853.toolbox.PublicKeyUtils;
import eu.europa.ec.markt.dss.validation102853.xades.XMLDocumentValidator;

/**
 * Validate the signed document
 *
 * @version $Revision: 889 $ - $Date: 2011-05-31 17:29:35 +0200 (Tue, 31 May 2011) $
 */
public abstract class SignedDocumentValidator {

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

    /*
     * 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 = new CertificatePool();

    /**
     * This is the unique timestamp Id. It is unique within one validation process.
     */
    private int timestampIndex = 1;

    /**
     * The document to be validated (with the signatures)
     */
    protected DSSDocument document;

    /**
     * In case of a detached signature this is the signed document.
     */
    protected DSSDocument externalContent;

    /**
     * The reference to the certificate verifier. The current DSS implementation proposes
     * {@link CommonCertificateVerifier}. This verifier encapsulates the references to different sources used in the signature validation
     * process.
     */
    private CertificateVerifier certVerifier;

    /**
     * This variable contains the reference to the diagnostic data.
     */
    protected DiagnosticData dData;

    /**
     * This is the simple report generated at the end of the validation process.
     */
    protected SimpleReport simpleReport;

    /**
     * This is the detailed report of the validation.
     */
    ValidationReport detailedReport;

    private final Condition qcp = new PolicyIdCondition(OID._0_4_0_1456_1_2.getName());

    private final Condition qcpplus = new PolicyIdCondition(OID._0_4_0_1456_1_1.getName());

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

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

    private static final String MIMETYPE = "mimetype";

    // private static final String MIMETYPE_ASIC_S = "application/vnd.etsi.asic-s+zip";
    private static final String PATTERN_SIGNATURES_XML = "META-INF/(.*)(?i)signature(.*).xml";

    private static final String PATTERN_SIGNATURES_P7S = "META-INF/(.*)(?i)signature(.*).p7s";

    /**
     * Guess the document format and return an appropriate document
     *
     * @param document The instance of DSSDocument to be validated
     * @return returns the specific instance of SignedDocumentValidator in terms of the document type
     */
    public static SignedDocumentValidator fromDocument(final DSSDocument document) throws IOException {

        InputStream input = null;
        try {

            if (document.getName() != null && document.getName().toLowerCase().endsWith(".xml")) {

                try {

                    return new XMLDocumentValidator(document);
                } catch (ParserConfigurationException e) {
                    throw new IOException("Not a valid XML", e);
                } catch (SAXException e) {
                    throw new IOException("Not a valid XML", e);
                }
            }

            input = new BufferedInputStream(document.openStream());
            input.mark(5);
            byte[] preamble = new byte[5];
            int read = input.read(preamble);
            input.reset();
            if (read < 5) {

                throw new RuntimeException("Not a signed document");
            }
            String preambleString = new String(preamble);
            byte[] xmlPreamble = new byte[] { '<', '?', 'x', 'm', 'l' };
            byte[] xmlUtf8 = new byte[] { -17, -69, -65, '<', '?' };
            if (Arrays.equals(preamble, xmlPreamble) || Arrays.equals(preamble, xmlUtf8)) {

                try {

                    return new XMLDocumentValidator(document);
                } catch (ParserConfigurationException e) {
                    throw new IOException("Not a valid XML", e);
                } catch (SAXException e) {
                    throw new IOException("Not a valid XML", e);
                }
            } else if (preambleString.equals("%PDF-")) {

                return new PDFDocumentValidator(document);
            } else if (preamble[0] == 'P' && preamble[1] == 'K') {

                try {

                    input.close();
                } catch (IOException e) {
                }
                input = null;
                return getInstanceForAsics(document);
            } else if (preambleString.getBytes()[0] == 0x30) {

                try {

                    return new CMSDocumentValidator(document);
                } catch (CMSException e) {
                    throw new IOException("Not a valid CAdES file", e);
                }
            } else {
                throw new RuntimeException("Document format not recognized/handled");
            }
        } finally {
            if (input != null) {
                try {
                    input.close();
                } catch (IOException e) {
                }
            }
        }
    }

    /**
     * @param document
     * @return
     * @throws IOException
     */
    private static SignedDocumentValidator getInstanceForAsics(DSSDocument document) throws IOException {

        ZipInputStream asics = new ZipInputStream(document.openStream());

        try {

            String dataFileName = "";
            ByteArrayOutputStream dataFile = null;
            ByteArrayOutputStream signatures = null;
            ZipEntry entry;

            boolean cadesSigned = false;
            boolean xadesSigned = false;

            while ((entry = asics.getNextEntry()) != null) {
                if (entry.getName().matches(PATTERN_SIGNATURES_P7S)) {
                    if (xadesSigned) {
                        throw new NotETSICompliantException(MSG.MORE_THAN_ONE_SIGNATURE);
                    }
                    signatures = new ByteArrayOutputStream();
                    IOUtils.copy(asics, signatures);
                    signatures.close();
                    cadesSigned = true;
                } else if (entry.getName().matches(PATTERN_SIGNATURES_XML)) {
                    if (cadesSigned) {
                        throw new NotETSICompliantException(MSG.MORE_THAN_ONE_SIGNATURE);
                    }
                    signatures = new ByteArrayOutputStream();
                    IOUtils.copy(asics, signatures);
                    signatures.close();
                    xadesSigned = true;
                } else if (entry.getName().equalsIgnoreCase(MIMETYPE)) {
                    ByteArrayOutputStream mimetype = new ByteArrayOutputStream();
                    IOUtils.copy(asics, mimetype);
                    mimetype.close();
                    // Mime type implementers MAY use
                    // "application/vnd.etsi.asic-s+zip" to identify this format
                    // or MAY
                    // maintain the original mimetype of the signed data object.
                } else if (entry.getName().indexOf("/") == -1) {
                    if (dataFile == null) {

                        dataFile = new ByteArrayOutputStream();
                        IOUtils.copy(asics, dataFile);
                        dataFile.close();
                        dataFileName = entry.getName();
                    } else {
                        throw new ProfileException("ASiC-S profile support only one data file");
                    }
                }
            }

            if (xadesSigned) {
                ASiCXMLDocumentValidator xmlValidator = new ASiCXMLDocumentValidator(
                        new InMemoryDocument(signatures.toByteArray()), dataFile.toByteArray(), dataFileName);
                return xmlValidator;
            } else if (cadesSigned) {
                CMSDocumentValidator pdfValidator = new CMSDocumentValidator(
                        new InMemoryDocument(signatures.toByteArray()));
                pdfValidator.setExternalContent(new InMemoryDocument(dataFile.toByteArray()));
                return pdfValidator;
            } else {
                throw new RuntimeException("Is not xades nor cades signed");
            }

        } catch (Exception ex) {
            throw new RuntimeException(ex);
        } finally {
            try {
                asics.close();
            } catch (IOException e) {
            }
        }

    }

    /*
     * In case of ASiC, this is the signature
     */
    public DSSDocument getDocument() {

        return document;
    }

    /**
     * @return the externalContent
     */
    public DSSDocument getExternalContent() {

        return externalContent;
    }

    /**
     * Retrieves the signatures found in the document
     *
     * @return a list of AdvancedSignatures for validation purposes
     */
    public abstract List<AdvancedSignature> getSignatures();

    /**
     * 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 certVerifier
     */
    public void setCertificateVerifier(final CertificateVerifier certVerifier) {

        this.certVerifier = certVerifier;

        TrustedCertificateSource trustedCertSource = certVerifier.getTrustedCertSource();
        if (trustedCertSource != null) {

            validationCertPool.merge(trustedCertSource.getCertificatePool());
        }
        CertificateSource adjunctCertSource = certVerifier.getAdjunctCertSource();
        if (adjunctCertSource != null) {

            validationCertPool.merge(adjunctCertSource.getCertificatePool());
        }
    }

    /**
     * Sets the Document containing the original content to sign, for detached signature scenarios.
     *
     * @param externalContent the externalContent to set
     */
    public void setExternalContent(final DSSDocument externalContent) {

        this.externalContent = externalContent;
    }

    /**
     * Validates the document and all its signatures. The default constraint file is used.
     */
    public ValidationReport validateDocument() {

        return validateDocument((InputStream) null);
    }

    /**
     * Validates the document and all its signatures. The default constraint file is used.
     */
    public ValidationReport validateDocument(URL validationPolicyURL) {
        if (validationPolicyURL == null) {
            return validateDocument((InputStream) null);
        } else {
            try {
                return validateDocument(validationPolicyURL.openStream());
            } catch (IOException e) {
                throw new DSSException(e);
            }
        }
    }

    /**
     * Validates the document and all its signatures. The policyResourcePath specifies the constraint file. If null or
     * empty the default file is used.
     *
     * @param policyResourcePath is located against the classpath (getClass().getResourceAsStream), and NOT the
     *                           filesystem
     */
    public ValidationReport validateDocument(final String policyResourcePath) {

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

    /**
     * Validates the document and all its signatures. The policyDataPath specifies the constraint file. If null or empty
     * the default file is used.
     *
     * @param policyDataStream
     */
    public ValidationReport validateDocument(final InputStream policyDataStream) {

        LOG.info("Document validation...");

        final DiagnosticData diagnosticDataJB = generateDiagnosticData();

        // Comment before release !!!
        // ValidationResourceManager.enableSaveDiagnosticData();
        // ValidationResourceManager.setDiagnosticDataFolder("");
        // End !!!

        ValidationResourceManager.saveDiagnosticData(diagnosticDataJB);

        final Document diagnosticData = ValidationResourceManager.convert(diagnosticDataJB);

        final Document policyData = ValidationResourceManager.loadPolicyData(policyDataStream);
        // TODO 130619 by meyerfr: create an interface for process executor and derive a BSCProcessExecutor from that
        final ProcessExecutor executor = getProcessExecutor(diagnosticData, policyData);
        detailedReport = executor.execute();
        simpleReport = executor.getSimpleReport();
        return detailedReport;
    }

    protected ProcessExecutor getProcessExecutor(Document diagnosticData, Document policyData) {
        return new ProcessExecutor(diagnosticData, policyData);
    }

    /**
     * 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() {

        dData = DIAGNOSTIC_DATA_OBJECT_FACTORY.createDiagnosticData();
        dData.setDocumentName(document.getAbsolutePath());
        // dData.setDocumentType("");
        if (this instanceof XMLDocumentValidator) {

            dData.setSignatureFormat("XAdES");
        } else if (this instanceof PDFDocumentValidator) {

            dData.setSignatureFormat("PAdES");
        } else if (this instanceof CMSDocumentValidator) {

            dData.setSignatureFormat("CAdES");
        } else if (this instanceof ASiCXMLDocumentValidator) {

            dData.setSignatureFormat("ASiC-S");
        }
        final Set<DigestAlgorithm> usedCertificatesDigestAlgorithms = new HashSet<DigestAlgorithm>();

        final Set<CertificateToken> usedCertPool = new HashSet<CertificateToken>();
        /*
         * For each signature present in the file to be validated the extraction of diagnostic data is launched.
         */
        for (final AdvancedSignature signature : getSignatures()) {

            final ValidationContext valContext = new SignatureValidationContext(signature, certVerifier,
                    validationCertPool);
            final XmlSignature xmlSignature = validateSignature(signature, valContext);
            final Set<CertificateToken> signatureCertPool = valContext.getProcessedCertificates();
            usedCertPool.addAll(signatureCertPool);
            usedCertificatesDigestAlgorithms.addAll(signature.getUsedCertificatesDigestAlgorithms());
            dData.getSignature().add(xmlSignature);
        }
        dealUsedCertificates(usedCertificatesDigestAlgorithms, usedCertPool);
        return dData;
    }

    /**
     * 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, final ValidationContext valContext)
            throws DSSException {

        /*
         * TODO: (Bob 20130424) The the certToValidate parameter must be added.
         */
        final XmlSignature xmlSignature = DIAGNOSTIC_DATA_OBJECT_FACTORY.createXmlSignature();

        try {

            final CertificateToken signingToken = dealSignature(signature, xmlSignature);

            valContext.setCertificateToValidate(signingToken);

            valContext.validate();

            dealPolicy(signature, xmlSignature);

            dealCertificateChain(xmlSignature, signingToken);

            dealTimestamps(xmlSignature, valContext.getTimestampTokens());

            dealContentTimestamps(signature, xmlSignature);

            dealSigAndRefsTimestamp(xmlSignature, valContext.getSigAndRefsTimestamps());

            dealRefsOnlyTimestamp(xmlSignature, valContext.getRefsOnlyTimestamps());

            dealArchiveTimestamp(xmlSignature, valContext.getArchiveTimestamps());
        } catch (Exception e) {

            LOG.warning(e.toString() + "\n" + e.getStackTrace()[0].toString());
            String errorMessage = xmlSignature.getErrorMessage();
            if (errorMessage == null || errorMessage.isEmpty()) {

                xmlSignature.setErrorMessage(e.toString());
            } else {

                errorMessage += "<br>" + e.toString();
            }
        }
        return xmlSignature;
    }

    /**
     * @param xmlSignature
     * @param timestampTokens
     */
    private void dealTimestamps(XmlSignature xmlSignature, List<TimestampToken> timestampTokens) {

        if (!timestampTokens.isEmpty()) {

            XmlTimestamps xmlTimestamps = DIAGNOSTIC_DATA_OBJECT_FACTORY.createXmlTimestamps();
            for (TimestampToken token : timestampTokens) {

                XmlTimestampType xmlTimestampToken = xmlForTimestamp(token, TimestampType.SIGNATURE_TIMESTAMP);
                xmlTimestamps.getTimestamp().add(xmlTimestampToken);
            }
            xmlSignature.setTimestamps(xmlTimestamps);
        }
    }

    /**
     * @param signature
     * @param xmlSignature
     */
    private void dealContentTimestamps(AdvancedSignature signature, XmlSignature xmlSignature) {

        List<TimestampToken> contentTimestampTokens = signature.getContentTimestamps();
        if (!contentTimestampTokens.isEmpty()) {

            XmlContentTimestamps xmlContentTimestamps = DIAGNOSTIC_DATA_OBJECT_FACTORY.createXmlContentTimestamps();
            for (TimestampToken timestampToken : contentTimestampTokens) {

                XMLGregorianCalendar calendar = DSSUtils
                        .createXMGregorianCalendar(timestampToken.getGenerationTime());
                xmlContentTimestamps.getProductionTime().add(calendar);
            }
            xmlSignature.setContentTimestamps(xmlContentTimestamps);
        }
    }

    private void dealSigAndRefsTimestamp(XmlSignature xmlSignature, List<TimestampToken> timestampTokens) {

        if (!timestampTokens.isEmpty()) {

            XmlSigAndRefsTimestamps xmlSigAndRefsTimestamps = DIAGNOSTIC_DATA_OBJECT_FACTORY
                    .createXmlSigAndRefsTimestamps();
            for (TimestampToken token : timestampTokens) {

                XmlTimestampType xmlTimestampToken = xmlForTimestamp(token,
                        TimestampType.VALIDATION_DATA_TIMESTAMP);
                xmlSigAndRefsTimestamps.getTimestamp().add(xmlTimestampToken);
            }
            xmlSignature.setSigAndRefsTimestamps(xmlSigAndRefsTimestamps);
        }
    }

    private void dealRefsOnlyTimestamp(XmlSignature xmlSignature, List<TimestampToken> timestampTokens) {

        if (!timestampTokens.isEmpty()) {

            XmlRefsOnlyTimestamps xmlRefsOnlyTimestamps = DIAGNOSTIC_DATA_OBJECT_FACTORY
                    .createXmlRefsOnlyTimestamps();
            for (TimestampToken token : timestampTokens) {

                XmlTimestampType xmlTimestampToken = xmlForTimestamp(token,
                        TimestampType.VALIDATION_DATA_REFSONLY_TIMESTAMP);
                xmlRefsOnlyTimestamps.getTimestamp().add(xmlTimestampToken);
            }
            xmlSignature.setRefsOnlyTimestamps(xmlRefsOnlyTimestamps);
        }
    }

    private void dealArchiveTimestamp(XmlSignature xmlSignature, List<TimestampToken> timestampTokens) {

        if (!timestampTokens.isEmpty()) {

            XmlArchiveTimestamps xmlArchiveTimestamps = DIAGNOSTIC_DATA_OBJECT_FACTORY.createXmlArchiveTimestamps();
            for (TimestampToken token : timestampTokens) {

                XmlTimestampType xmlTimestampToken = xmlForTimestamp(token, TimestampType.ARCHIVE_TIMESTAMP);
                xmlArchiveTimestamps.getTimestamp().add(xmlTimestampToken);
            }
            xmlSignature.setArchiveTimestamps(xmlArchiveTimestamps);
        }
    }

    /**
     * @param token
     * @return
     */
    private XmlTimestampType xmlForTimestamp(TimestampToken token, TimestampType timestampCategory) {

        TimestampToken timestampToken = (TimestampToken) token;
        XmlTimestampType xmlTimestampToken = DIAGNOSTIC_DATA_OBJECT_FACTORY.createXmlTimestampType();
        xmlTimestampToken.setId(timestampIndex++);
        xmlTimestampToken.setCategory(timestampCategory.name());
        xmlTimestampToken.setProductionTime(DSSUtils.createXMGregorianCalendar(timestampToken.getGenerationTime()));
        xmlTimestampToken.setAlgoUsedToSignThisToken(timestampToken.getSignatureAlgo());
        String oid = timestampToken.getSignatureAlgoOID();
        if (oid != null) {

            SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.forOID(oid);
            xmlTimestampToken.setAlgoOIDUsedToSignThisToken(oid);
            xmlTimestampToken
                    .setEncryptionAlgoUsedToSignThisToken(signatureAlgorithm.getEncryptionAlgo().getName());
            xmlTimestampToken.setDigestAlgoUsedToSignThisToken(signatureAlgorithm.getDigestAlgo().getName());
        }
        String keyLength = getSigningKeyLength(timestampToken);
        xmlTimestampToken.setKeyLengthUsedToSignThisToken(keyLength);

        xmlTimestampToken.setSignedDataDigestAlgo(timestampToken.getSignedDataDigestAlgo().getName());
        xmlTimestampToken.setEncodedSignedDataDigestValue(timestampToken.getEncodedSignedDataDigestValue());
        xmlTimestampToken.setReferenceDataFound(timestampToken.isSignedDataFound());
        xmlTimestampToken.setReferenceDataIntact(timestampToken.isSignedDataIntact());
        xmlTimestampToken.setSignatureIntact(timestampToken.isSignatureIntact());
        XmlSigningCertificateType xmlTSSignCert = xmlForSigningCertificate(timestampToken);
        xmlTimestampToken.setSigningCertificate(xmlTSSignCert);

        CertificateToken issuerToken = timestampToken.getIssuerToken();
        if (issuerToken != null) {
            XmlCertificateChainType xmlCertChainType = xmlForCertificateChain(issuerToken);
            xmlTimestampToken.setCertificateChain(xmlCertChainType);
        }

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

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

            for (TimestampReference timestampReference : timestampReferences) {

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

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

                    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(CertificateToken issuerToken) {

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

            XmlChainCertificate xmlCertToken = DIAGNOSTIC_DATA_OBJECT_FACTORY.createXmlChainCertificate();
            xmlCertToken.setId(issuerToken.getDSSId());
            List<CertificateSourceType> list = issuerToken.getSource();
            if (list.size() > 0) {

                xmlCertToken.setSource(list.get(0).name());
            } else {

                xmlCertToken.setSource("UNKNOWN");
            }
            certChainTokens.add(xmlCertToken);
            if (issuerToken.isTrusted() || issuerToken.isSelfSigned()) {

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

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

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

            final XmlCertificate xmlCert = dealCertificateDetails(usedCertificatesDigestAlgorithms, certToken);
            // !!! Log the certificate
            // if (LOG.isLoggable(Level.INFO)) {
            //
            // LOG.info("PEM for certificate: " + certToken.getAbbreviation() + "--->");
            // try {
            //
            // final String pem = DSSUtils.convertToPEM(certToken.getCertificate());
            // LOG.info("\n" + pem);
            // } catch (CertificateEncodingException e) {
            // }
            // }
            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 X509Certificate cert = certToken.getCertificate();
            final XmlQCStatement xmlQCS = DIAGNOSTIC_DATA_OBJECT_FACTORY.createXmlQCStatement();
            xmlQCS.setQCP(qcp.check(cert));
            xmlQCS.setQCPPlus(qcpplus.check(cert));
            xmlQCS.setQCC(qccompliance.check(cert));
            xmlQCS.setQCSSCD(qcsscd.check(cert));
            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 certToken
     * @return
     */
    private XmlCertificate dealCertificateDetails(final Set<DigestAlgorithm> usedCertificatsDigestAlgorithms,
            final CertificateToken certToken) {

        final XmlCertificate xmlCert = DIAGNOSTIC_DATA_OBJECT_FACTORY.createXmlCertificate();

        xmlCert.setId(certToken.getDSSId());
        final XmlSubjectDistinguishedName xmlSubject = DIAGNOSTIC_DATA_OBJECT_FACTORY
                .createXmlSubjectDistinguishedName();
        xmlSubject.setFormat(X500Principal.CANONICAL);
        xmlSubject.setValue(certToken.getSubjectX500Principal().getName(X500Principal.CANONICAL));
        xmlCert.setSubjectDistinguishedName(xmlSubject);

        final XmlIssuerDistinguishedName xmlIssuer = DIAGNOSTIC_DATA_OBJECT_FACTORY
                .createXmlIssuerDistinguishedName();
        xmlIssuer.setFormat(X500Principal.CANONICAL);
        xmlIssuer.setValue(certToken.getIssuerX500Principal().getName(X500Principal.CANONICAL));
        xmlCert.setIssuerDistinguishedName(xmlIssuer);
        xmlCert.setSerialNumber(certToken.getSerialNumber());

        for (final DigestAlgorithm digestAlgorithm : usedCertificatsDigestAlgorithms) {

            final XmlDigestAlgAndValueType xmlDigestAlgAndValue = new XmlDigestAlgAndValueType();
            xmlDigestAlgAndValue.setDigestMethod(digestAlgorithm.getName());
            xmlDigestAlgAndValue.setDigestValue(certToken.getDigestValue(digestAlgorithm));
            xmlCert.getDigestAlgAndValue().add(xmlDigestAlgAndValue);
        }
        xmlCert.setIssuerCertificate(certToken.getIssuerTokenDSSId());
        xmlCert.setNotAfter(DSSUtils.createXMGregorianCalendar(certToken.getNotAfter()));
        xmlCert.setNotBefore(DSSUtils.createXMGregorianCalendar(certToken.getNotBefore()));
        final PublicKey publicKey = certToken.getPublicKey();
        xmlCert.setPublicKeySize(PublicKeyUtils.getPublicKeySize(publicKey));
        xmlCert.setPublicKeyEncryptionAlgo(PublicKeyUtils.getPublicKeyEncryptionAlgo(publicKey));
        xmlCert.setAlgoUsedToSignThisToken(certToken.getSignatureAlgo());
        final String signatureAlgoOID = certToken.getSignatureAlgoOID();
        xmlCert.setAlgoOIDUsedToSignThisToken(signatureAlgoOID);
        final SignatureAlgorithm signatureAlgo = SignatureAlgorithm.forOID(signatureAlgoOID);
        xmlCert.setDigestAlgoUsedToSignThisToken(signatureAlgo.getDigestAlgo().getName());
        xmlCert.setEncryptionAlgoUsedToSignThisToken(signatureAlgo.getEncryptionAlgo().getName());
        final String keyLength = getSigningKeyLength((Token) certToken);
        xmlCert.setKeyLengthUsedToSignThisToken(keyLength);
        xmlCert.setSelfSigned(certToken.isSelfSigned());
        xmlCert.setTrusted(certToken.isTrusted());
        xmlCert.setTokenSignatureIntact(certToken.isSignatureIntact());

        return xmlCert;
    }

    /**
     * This method return a key length used to sign the given certificate token.
     *
     * @param certToken
     * @return
     */
    private String getSigningKeyLength(CertificateToken certToken) {

        String keyLength = "";
        if (certToken != null) {

            final PublicKey issuerPublicKey = certToken.getPublicKey();
            keyLength = String.valueOf(PublicKeyUtils.getPublicKeySize(issuerPublicKey));
        }
        return keyLength;
    }

    /**
     * This method return a key length used to sign the given certificate token.
     *
     * @param certToken
     * @return
     */
    private String getSigningKeyLength(Token certToken) {

        final CertificateToken issuerCertificateToken = certToken.getIssuerToken();
        String keyLength = "";
        if (issuerCertificateToken != null) {

            final PublicKey issuerPublicKey = issuerCertificateToken.getPublicKey();
            keyLength = String.valueOf(PublicKeyUtils.getPublicKeySize(issuerPublicKey));
        } else if (certToken.isSelfSigned()) {

            final PublicKey issuerPublicKey = ((CertificateToken) certToken).getPublicKey();
            keyLength = String.valueOf(PublicKeyUtils.getPublicKeySize(issuerPublicKey));
        }
        return keyLength;
    }

    /**
     * This method deals with the certificate chain. The retrieved information is transformed to the JAXB object.
     *
     * @param xmlSignature
     * @param signToken
     */
    private void dealCertificateChain(final XmlSignature xmlSignature, final CertificateToken signToken) {

        if (signToken != null) {

            final XmlCertificateChainType xmlCertChainType = xmlForCertificateChain(signToken);
            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 Date notBefore = certToken.getNotBefore();

        final XmlTrustedServiceProviderType xmlTSP = DIAGNOSTIC_DATA_OBJECT_FACTORY
                .createXmlTrustedServiceProviderType();
        final List<ServiceInfo> services = trustAnchor.getAssociatedTSPS();
        if (services == null) {

            return;
        }
        boolean first = true;
        for (final ServiceInfo serviceInfo : services) {

            if (first) {

                xmlTSP.setTSPName(serviceInfo.getTspName());
                xmlTSP.setTSPServiceName(serviceInfo.getServiceName());
                xmlTSP.setTSPServiceType(serviceInfo.getType());
                xmlTSP.setWellSigned(serviceInfo.isTlWellSigned());
                first = false;
            }
            final Date statusStartDate = serviceInfo.getStatusStartDate();
            Date statusEndDate = serviceInfo.getStatusEndDate();
            if (statusEndDate == null) {

                // TODO: Should be changed in the case it would be possible to carry out the validation process at a specific moment in the time (validation date)
                statusEndDate = new Date();
            }
            // The issuing time of the certificate should be into the validity period of the associated service
            if (notBefore.after(statusStartDate) && notBefore.before(statusEndDate)) {

                xmlTSP.setStatus(serviceInfo.getStatus());
                xmlTSP.setStartDate(DSSUtils.createXMGregorianCalendar(statusStartDate));
                xmlTSP.setEndDate(DSSUtils.createXMGregorianCalendar(serviceInfo.getStatusEndDate()));
                xmlTSP.setExpiredCertsRevocationInfo(
                        DSSUtils.createXMGregorianCalendar(serviceInfo.getExpiredCertsRevocationInfo()));

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

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

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

    /**
     * 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) {

            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(DSSUtils.createXMGregorianCalendar(revocationToken.getRevocationDate()));
            xmlRevocation.setReason(revocationToken.getReason());
            xmlRevocation.setIssuingTime(DSSUtils.createXMGregorianCalendar(revocationToken.getIssuingTime()));
            xmlRevocation.setNextUpdate(DSSUtils.createXMGregorianCalendar(revocationToken.getNextUpdate()));
            xmlRevocation.setSource(revocationToken.getClass().getSimpleName());
            xmlRevocation.setSourceAddress(revocationToken.getSourceURI());

            final XmlSigningCertificateType xmlRevocationSignCert = xmlForSigningCertificate(revocationToken);
            xmlRevocation.setSigningCertificate(xmlRevocationSignCert);
            final String oid = revocationToken.getSignatureAlgoOID();
            final SignatureAlgorithm revocationSignatureAlgo = SignatureAlgorithm.forOID(oid);
            xmlRevocation.setAlgoOIDUsedToSignThisToken(oid);
            xmlRevocation.setAlgoUsedToSignThisToken(revocationToken.getSignatureAlgo());
            xmlRevocation
                    .setEncryptionAlgoUsedToSignThisToken(revocationSignatureAlgo.getEncryptionAlgo().getName());
            final String keyLength = getSigningKeyLength(revocationToken);
            xmlRevocation.setKeyLengthUsedToSignThisToken(keyLength);
            xmlRevocation.setDigestAlgoUsedToSignThisToken(revocationSignatureAlgo.getDigestAlgo().getName());
            xmlRevocation.setReferenceDataFound(revocationToken.isSignatureIntact());
            xmlRevocation.setReferenceDataIntact(revocationToken.isSignatureIntact());
            xmlRevocation.setSignatureIntact(revocationToken.isSignatureIntact());

            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
     * @param xmlSignature
     */
    private void dealPolicy(final AdvancedSignature signature, final XmlSignature xmlSignature) {

        final PolicyValue policy = signature.getPolicyId();
        if (policy != null) {

            final XmlPolicy xmlPolicy = DIAGNOSTIC_DATA_OBJECT_FACTORY.createXmlPolicy();
            xmlPolicy.setId(policy.getSignaturePolicyId());
            xmlPolicy.setIdentified(true);
            xmlPolicy.setStatus(true);
            xmlSignature.setPolicy(xmlPolicy);
        }
    }

    /**
     * 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
     * @param xmlSignature
     * @return
     */
    private CertificateToken dealSignature(final AdvancedSignature signature, final XmlSignature xmlSignature) {

        final SigningCertificate signingCertificate = dealSigningCertificate(signature, xmlSignature);
        dealSignatureCryptographicIntegrity(signature, xmlSignature);
        xmlSignature.setId(signature.getId());
        xmlSignature.setDateTime(DSSUtils.createXMGregorianCalendar(signature.getSigningTime()));
        final SignatureProductionPlace signatureProductionPlace = signature.getSignatureProductionPlace();
        if (signatureProductionPlace != null) {

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

        final String[] claimedRoles = signature.getClaimedSignerRoles();
        if (claimedRoles != null) {

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

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

        xmlSignature.setEncryptionAlgoUsedToSignThisToken(signature.getEncryptionAlgo().getName());
        final String keyLength = getSigningKeyLength(signingCertificate.getCertToken());
        xmlSignature.setKeyLengthUsedToSignThisToken(keyLength);
        xmlSignature.setDigestAlgoUsedToSignThisToken(signature.getDigestAlgo().getName());
        return signingCertificate.getCertToken();
    }

    /**
     * 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
     * @param xmlSignature
     */
    private void dealSignatureCryptographicIntegrity(final AdvancedSignature signature,
            final XmlSignature xmlSignature) {

        final SignatureCryptographicVerification scv = signature.checkIntegrity(this.externalContent);
        xmlSignature.setReferenceDataFound(scv.isReferenceDataFound());
        xmlSignature.setReferenceDataIntact(scv.isReferenceDataIntact());
        xmlSignature.setSignatureIntact(scv.isSignatureIntact());
        if (!scv.getErrorMessage().isEmpty()) {

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

    /**
     * This method finds the signing certificate and creates its JAXB object representation. The signing certificate used
     * to produce the main signature (signature being analysed). If the signToken is null (the signing certificate was
     * not found) then Id is set to 0.
     *
     * @param signature
     * @param xmlSignature
     * @return
     */
    private SigningCertificate dealSigningCertificate(final AdvancedSignature signature,
            final XmlSignature xmlSignature) {

        final SigningCertificate signCert = signature.getSigningCertificate();
        final XmlSigningCertificateType xmlSignCertType = DIAGNOSTIC_DATA_OBJECT_FACTORY
                .createXmlSigningCertificateType();
        if (signCert.getCertToken() != null) {

            xmlSignCertType.setId(signCert.getCertToken().getDSSId());
        }
        xmlSignCertType.setDigestValueMatch(signCert.isDigestMatch());
        xmlSignCertType.setIssuerSerialMatch(signCert.isSerialNumberMatch());
        xmlSignature.setSigningCertificate(xmlSignCertType);
        return signCert;
    }

    /*
        TODO: (Bob) Old code to be adapted when we are ready to handle the countersignatures.
        
        protected SignatureVerification[] verifyCounterSignatures(final AdvancedSignature signature, final ValidationContext ctx) {
        
    final List<AdvancedSignature> counterSignatures = signature.getCounterSignatures();
        
    if (counterSignatures == null) {
        return null;
    }
        
    final List<SignatureVerification> counterSigVerifs = new ArrayList<SignatureVerification>();
    for (final AdvancedSignature counterSig : counterSignatures) {
        
        final Result counterSigResult;
        try {
        
            final SignatureCryptographicVerification scv = counterSig.checkIntegrity(getExternalContent());
            counterSigResult = new Result(scv.signatureIntact());
        } catch (DSSException e) {
            throw new RuntimeException(e);
        }
        final String counterSigAlg = counterSig.getEncryptionAlgo().getName();
        counterSigVerifs.add(new SignatureVerification(counterSigResult, counterSigAlg, signature.getId()));
    }
        
    final SignatureVerification[] ret = new SignatureVerification[counterSigVerifs.size()];
    return counterSigVerifs.toArray(ret);
        }
    */

    /**
     * This method creates the XML object representing the signing certificate used to sign the revocation data (OCSP or
     * CRL). If the signToken is null (the signing certificate was not found) then Id is set to 0.
     *
     * @param revocationToken
     * @return
     */
    protected XmlSigningCertificateType xmlForSigningCertificate(final RevocationToken revocationToken) {

        final XmlSigningCertificateType xmlSignCertType = DIAGNOSTIC_DATA_OBJECT_FACTORY
                .createXmlSigningCertificateType();
        final CertificateToken certToken = revocationToken.getIssuerToken();
        if (certToken != null) {

            xmlSignCertType.setId(certToken.getDSSId());
        }
        /**
         * FIXME: The fact that it is not possible to validate the CAdES signature following the ETSI TS 102 853 standard
         * requires us set the DigestValueMatch and IssuerSerialMatch to the same value as the result of the signature
         * validation.
         */
        xmlSignCertType.setDigestValueMatch(revocationToken.isSignatureIntact());
        xmlSignCertType.setIssuerSerialMatch(revocationToken.isSignatureIntact());
        return xmlSignCertType;
    }

    /**
     * This method creates the XML object representing the signing certificate used to sign the timestamp token. If the
     * signToken is null (the signing certificate was not found) then Id is set to 0.
     *
     * @param timestampToken
     * @return
     */
    protected XmlSigningCertificateType xmlForSigningCertificate(final TimestampToken timestampToken) {

        final XmlSigningCertificateType xmlSignCertType = DIAGNOSTIC_DATA_OBJECT_FACTORY
                .createXmlSigningCertificateType();
        final CertificateToken certToken = timestampToken.getIssuerToken();
        if (certToken != null) {

            xmlSignCertType.setId(certToken.getDSSId());
        }
        /**
         * FIXME: The fact that it is not possible to validate the CAdES signature following the ETSI TS 102 853 standard
         * requires us set the DigestValueMatch and IssuerSerialMatch to the same value as the result of the signature
         * validation.
         */
        xmlSignCertType.setDigestValueMatch(timestampToken.isSignatureIntact());
        xmlSignCertType.setIssuerSerialMatch(timestampToken.isSignatureIntact());
        return xmlSignCertType;
    }

    /**
     * @return The diagnostic data generated by the validateDocument method
     */
    public DiagnosticData getDiagnosticData() {

        return dData;
    }

    /**
     * Returns the simple report. The method {@link #validateDocument()} or {@link #validateDocument(String)} must be
     * called first.
     *
     * @return
     */
    public SimpleReport getSimpleReport() {
        return simpleReport;
    }

    /**
     * Returns the detailed report. The method {@link #validateDocument()} or {@link #validateDocument(String)} must be
     * called first.
     *
     * @return
     */
    public ValidationReport getDetailedReport() {
        return detailedReport;
    }

    /**
     * Output to System.out the diagnosticData, detailledReport and SimpleReport.
     */
    public void printReports() {
        System.out.println("----------------Diagnostic data-----------------");
        System.out.println(ValidationResourceManager.jaxbMarshalToOutputStream(getDiagnosticData()).toString());

        System.out.println("----------------Validation report---------------");
        System.out.println(getDetailedReport());

        System.out.println("----------------Simple report-------------------");
        System.out.println(getSimpleReport());

        System.out.println("------------------------------------------------");
    }

}