/* * 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.File; import java.io.IOException; import java.io.InputStream; import java.net.URL; import java.security.PublicKey; import java.security.cert.X509Certificate; import java.util.ArrayList; import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; import javax.security.auth.x500.X500Principal; 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.ec.markt.dss.CertificateIdentifier; import eu.europa.ec.markt.dss.DSSASN1Utils; import eu.europa.ec.markt.dss.DSSPKUtils; import eu.europa.ec.markt.dss.DSSUtils; import eu.europa.ec.markt.dss.DSSXMLUtils; import eu.europa.ec.markt.dss.DigestAlgorithm; import eu.europa.ec.markt.dss.EncryptionAlgorithm; 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.DSSNullException; import eu.europa.ec.markt.dss.exception.DSSUnsupportedOperationException; import eu.europa.ec.markt.dss.signature.DSSDocument; import eu.europa.ec.markt.dss.signature.MimeType; import eu.europa.ec.markt.dss.signature.SignatureLevel; import eu.europa.ec.markt.dss.validation102853.asic.ASiCContainerValidator; import eu.europa.ec.markt.dss.validation102853.bean.CandidatesForSigningCertificate; import eu.europa.ec.markt.dss.validation102853.bean.CertificateValidity; import eu.europa.ec.markt.dss.validation102853.bean.CertifiedRole; import eu.europa.ec.markt.dss.validation102853.bean.CommitmentType; 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.cades.CAdESSignature; import eu.europa.ec.markt.dss.validation102853.cades.CMSDocumentValidator; import eu.europa.ec.markt.dss.validation102853.certificate.CertificateSourceType; 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.crl.ListCRLSource; import eu.europa.ec.markt.dss.validation102853.data.diagnostic.ObjectFactory; import eu.europa.ec.markt.dss.validation102853.data.diagnostic.XmlBasicSignatureType; 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.XmlCertifiedRolesType; 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.XmlCommitmentTypeIndication; import eu.europa.ec.markt.dss.validation102853.data.diagnostic.XmlDigestAlgAndValueType; import eu.europa.ec.markt.dss.validation102853.data.diagnostic.XmlDistinguishedName; import eu.europa.ec.markt.dss.validation102853.data.diagnostic.XmlInfoType; import eu.europa.ec.markt.dss.validation102853.data.diagnostic.XmlKeyUsageBits; 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.XmlRevocationType; 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.XmlSignatureScopeType; import eu.europa.ec.markt.dss.validation102853.data.diagnostic.XmlSignatureScopes; 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.XmlStructuralValidationType; 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.loader.DataLoader; import eu.europa.ec.markt.dss.validation102853.ocsp.ListOCSPSource; import eu.europa.ec.markt.dss.validation102853.pades.PAdESSignature; import eu.europa.ec.markt.dss.validation102853.pades.PDFDocumentValidator; import eu.europa.ec.markt.dss.validation102853.policy.EtsiValidationPolicy; import eu.europa.ec.markt.dss.validation102853.policy.ValidationPolicy; import eu.europa.ec.markt.dss.validation102853.report.Reports; import eu.europa.ec.markt.dss.validation102853.rules.AttributeValue; import eu.europa.ec.markt.dss.validation102853.scope.SignatureScope; import eu.europa.ec.markt.dss.validation102853.scope.SignatureScopeFinder; import eu.europa.ec.markt.dss.validation102853.xades.XAdESSignature; import eu.europa.ec.markt.dss.validation102853.xades.XMLDocumentValidator; /** * Validate the signed document. The content of the document is determined automatically. It can be: XML, CAdES(p7m), PDF or ASiC(zip). * <p/> * SignatureScopeFinder can be set using the appropriate setter (ex. setCadesSignatureScopeFinder). By default, this class will use the * default SignatureScopeFinder as defined by eu.europa.ec.markt.dss.validation102853.scope.SignatureScopeFinderFactory * * @version $Revision: 889 $ - $Date: 2011-05-31 17:29:35 +0200 (Tue, 31 May 2011) $ */ public abstract class SignedDocumentValidator implements DocumentValidator { private static final Logger LOG = LoggerFactory.getLogger(SignedDocumentValidator.class); private static final byte[] xmlPreamble = new byte[]{'<', '?', 'x', 'm', 'l'}; private static final byte[] xmlUtf8 = new byte[]{-17, -69, -65, '<', '?'}; /** * This variable can hold a specific {@code ProcessExecutor} */ protected ProcessExecutor processExecutor = null; protected SignatureScopeFinder<CAdESSignature> cadesSignatureScopeFinder = null; protected SignatureScopeFinder<PAdESSignature> padesSignatureScopeFinder = null; protected SignatureScopeFinder<XAdESSignature> xadesSignatureScopeFinder = 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.ec.markt.dss.validation102853.CommonCertificateVerifier}. This verifier * encapsulates the references to different sources used in the signature validation process. */ protected CertificateVerifier certificateVerifier; /** * This list contains the list of signatures */ protected List<AdvancedSignature> signatures = null; /** * This variable contains the reference to the diagnostic data. */ protected eu.europa.ec.markt.dss.validation102853.data.diagnostic.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); private final Condition qcsscd = new QcStatementCondition(ETSIQCObjectIdentifiers.id_etsi_qcs_QcSSCD); // Single policy document to use with all signatures. private File policyDocument; private HashMap<String, File> policyDocuments; /** * 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) { final String dssDocumentName = dssDocument.getName(); if (dssDocumentName != null && MimeType.XML.equals(MimeType.fromFileName(dssDocumentName))) { return new XMLDocumentValidator(dssDocument); } int headerLength = 500; byte[] preamble = new byte[headerLength]; int read = DSSUtils.readToArray(dssDocument, headerLength, preamble); if (read < 5) { throw new DSSException("The signature is not found."); } final String preambleString = new String(preamble); if (isXmlPreamble(preamble)) { return new XMLDocumentValidator(dssDocument); } else if (preambleString.startsWith("%PDF-")) { // TODO (29/08/2014): DSS-356 return new PDFDocumentValidator(dssDocument); } else if (preamble[0] == 'P' && preamble[1] == 'K') { return ASiCContainerValidator.getInstanceForAsics(dssDocument); } else if (preambleString.getBytes()[0] == 0x30) { return new CMSDocumentValidator(dssDocument); } else { throw new DSSException("Document format not recognized/handled"); } } private static boolean isXmlPreamble(byte[] preamble) { return DSSUtils.equals(preamble, xmlPreamble, 5) || DSSUtils.equals(preamble, xmlUtf8, 5); } @Override public DSSDocument getDocument() { return document; } @Override public List<DSSDocument> getDetachedContents() { return detachedContents; } @Override public void defineSigningCertificate(final X509Certificate x509Certificate) { if (x509Certificate == null) { throw new DSSNullException(X509Certificate.class); } if (validationCertPool == null) { throw new DSSNullException(CertificatePool.class, "validationCertPool"); } 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 DSSNullException(CertificateVerifier.class); } 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 eu.europa.ec.markt.dss.validation102853.data.diagnostic.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 eu.europa.ec.markt.dss.validation102853.data.diagnostic.DiagnosticData generateDiagnosticData() { prepareDiagnosticData(); final List<AdvancedSignature> allSignatureList = getAllSignatures(); final ListCRLSource signatureCRLSource = getSignatureCrlSource(allSignatureList); certificateVerifier.setSignatureCRLSource(signatureCRLSource); final ListOCSPSource signatureOCSPSource = getSignatureOcspSource(allSignatureList); certificateVerifier.setSignatureOCSPSource(signatureOCSPSource); final ValidationContext validationContext = new SignatureValidationContext(certificateVerifier, validationCertPool); final ProcessExecutor processExecutor = provideProcessExecutorInstance(); final int concurrentThreadNumber = processExecutor.getConcurrentThreadNumber(); if (concurrentThreadNumber > 0) { final ExecutorService executorService = Executors.newFixedThreadPool(concurrentThreadNumber); validationContext.setExecutorService(executorService); } // The list of all signing certificates is created to allow a parallel validation. prepareCertificatesAndTimestamps(allSignatureList, validationContext); validationContext.setCurrentTime(processExecutor.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(); // To cope with tests it can be interesting to always keep the same file name within the reports (without the path). String absolutePath = document.getAbsolutePath(); if (CertificateIdentifier.isUniqueIdentifier()) { absolutePath = document.getName(); } jaxbDiagnosticData.setDocumentName(absolutePath); } /** * 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); } final List<TimestampToken> timestampTokenList = signature.prepareTimestamps(); for (final TimestampToken timestampToken : timestampTokenList) { validationContext.addTimestampTokenForVerification(timestampToken); } } } /** * 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 (DSSUtils.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()); 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 = timestampToken.getKeyLength(); 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()); 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 List<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.get(0); } } 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.getCertificate()); 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()); 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(certToken.getDigestValue(digestAlgorithm)); xmlCert.getDigestAlgAndValue().add(xmlDigestAlgAndValue); } xmlCert.setIssuerCertificate(certToken.getIssuerTokenDSSId()); 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 (certToken.isOCSPSigning()) { xmlCert.setIdKpOCSPSigning(true); } if (certToken.hasIdPkixOcspNoCheckExtension()) { xmlCert.setIdPkixOcspNoCheck(true); } if (certToken.hasExpiredCertOnCRLExtension()) { 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 = certToken.getKeyLength(); 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 List<String> keyUsageBits = certToken.getKeyUsageBits(); if (DSSUtils.isEmpty(keyUsageBits)) { return; } final XmlKeyUsageBits xmlKeyUsageBits = DIAGNOSTIC_DATA_OBJECT_FACTORY.createXmlKeyUsageBits(); final List<String> xmlKeyUsageBitItems = xmlKeyUsageBits.getKeyUsage(); for (final String keyUsageBit : keyUsageBits) { xmlKeyUsageBitItems.add(keyUsageBit); } 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 List<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 = serviceInfo.getQualifiers(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); // } } } /** * 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 = revocationToken.getKeyLength(); 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); /** * 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 && (policyUrl == null || policyUrl.isEmpty())) { 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.toString()); //Do nothing LOG.warn(e.toString()); return; } DigestAlgorithm signPolicyHashAlgFromPolicy = null; String policyDigestHexValueFromPolicy = null; String recalculatedDigestHexValue = null; /** * 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. * // TODO: (Bob: 2013 Dec 10) ETSI to be notified: it is signPolicyHashAlg and not SignPolicyDigestAlg */ try { final ASN1Sequence asn1Sequence = DSSASN1Utils.toASN1Primitive(policyBytes); final ASN1Sequence signPolicyHashAlgObject = (ASN1Sequence) asn1Sequence.getObjectAt(0); final AlgorithmIdentifier signPolicyHashAlgIdentifier = AlgorithmIdentifier.getInstance(signPolicyHashAlgObject); final String signPolicyHashAlgOID = signPolicyHashAlgIdentifier.getAlgorithm().getId(); signPolicyHashAlgFromPolicy = DigestAlgorithm.forOID(signPolicyHashAlgOID); final ASN1Sequence signPolicyInfo = (ASN1Sequence) asn1Sequence.getObjectAt(1); //signPolicyInfo.getObjectAt(1); final ASN1OctetString signPolicyHash = (ASN1OctetString) asn1Sequence.getObjectAt(2); final byte[] policyDigestValueFromPolicy = signPolicyHash.getOctets(); policyDigestHexValueFromPolicy = DSSUtils.toHex(policyDigestValueFromPolicy); final byte[] hashAlgorithmDEREncoded = DSSASN1Utils.getEncoded(signPolicyHashAlgIdentifier); final byte[] signPolicyInfoDEREncoded = DSSASN1Utils.getEncoded(signPolicyInfo); final byte[] recalculatedDigestValue = DSSUtils.digest(signPolicyHashAlgFromPolicy, hashAlgorithmDEREncoded, signPolicyInfoDEREncoded); recalculatedDigestHexValue = DSSUtils.toHex(recalculatedDigestValue); /** * 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. */ /** * c) * In all other cases, compute the digest using the digesting algorithm indicated in the children of the property/attribute. */ String policyDigestValueFromSignature = signaturePolicy.getDigestValue(); policyDigestValueFromSignature = policyDigestValueFromSignature.toUpperCase(); /** * 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. */ final DigestAlgorithm signPolicyHashAlgFromSignature = signaturePolicy.getDigestAlgorithm(); 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; } xmlPolicy.setDigestAlgorithmsEqual(true); 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; } 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 + ")."); } } catch (RuntimeException e) { // When any error (communication) we just set the status to false xmlPolicy.setStatus(false); xmlPolicy.setProcessingError(e.toString()); //Do nothing LOG.warn(e.toString()); } } /** * 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 : signingCertificateToken.getPublicKeyLength(); 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 = provideProcessExecutorInstance().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(DSSUtils.EMPTY.equals(structureValid)); if (!DSSUtils.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.getAddress()); 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 = getSignatureScopeFinder().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()); } 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()); return xmlSignCertType; } return null; } /** * @return {@code SignatureScopeFinder<XAdESSignature>} */ public SignatureScopeFinder<XAdESSignature> getXadesSignatureScopeFinder() { return xadesSignatureScopeFinder; } /** * Set the SignatureScopeFinder to use for XML signatures * * @param xadesSignatureScopeFinder */ public void setXadesSignatureScopeFinder(SignatureScopeFinder<XAdESSignature> xadesSignatureScopeFinder) { this.xadesSignatureScopeFinder = xadesSignatureScopeFinder; } public SignatureScopeFinder<CAdESSignature> getCadesSignatureScopeFinder() { return cadesSignatureScopeFinder; } /** * Set the SignatureScopeFinder to use for CMS signatures * * @param cadesSignatureScopeFinder */ public void setCadesSignatureScopeFinder(SignatureScopeFinder<CAdESSignature> cadesSignatureScopeFinder) { this.cadesSignatureScopeFinder = cadesSignatureScopeFinder; } public SignatureScopeFinder<PAdESSignature> getPadesSignatureScopeFinder() { return padesSignatureScopeFinder; } /** * Set the SignatureScopeFinder to use for PDF signatures * * @param padesSignatureScopeFinder */ public void setPadesSignatureScopeFinder(SignatureScopeFinder<PAdESSignature> padesSignatureScopeFinder) { this.padesSignatureScopeFinder = padesSignatureScopeFinder; } protected abstract SignatureScopeFinder getSignatureScopeFinder(); /** * 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; } }