Java tutorial
/* * DSS - Digital Signature Services * * Copyright (C) 2011 European Commission, Directorate-General Internal Market and Services (DG MARKT), B-1049 Bruxelles/Brussel * * Developed by: 2011 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.validation; import eu.europa.ec.markt.dss.NotETSICompliantException; import eu.europa.ec.markt.dss.NotETSICompliantException.MSG; import eu.europa.ec.markt.dss.signature.Document; import eu.europa.ec.markt.dss.signature.InMemoryDocument; import eu.europa.ec.markt.dss.signature.ProfileException; import eu.europa.ec.markt.dss.validation.asic.ASiCXMLDocumentValidator; import eu.europa.ec.markt.dss.validation.cades.CMSDocumentValidator; import eu.europa.ec.markt.dss.validation.certificate.CertificateAndContext; import eu.europa.ec.markt.dss.validation.pades.PDFDocumentValidator; import eu.europa.ec.markt.dss.validation.report.CertPathRevocationAnalysis; import eu.europa.ec.markt.dss.validation.report.QCStatementInformation; import eu.europa.ec.markt.dss.validation.report.QualificationsVerification; import eu.europa.ec.markt.dss.validation.report.Result; import eu.europa.ec.markt.dss.validation.report.Result.ResultStatus; import eu.europa.ec.markt.dss.validation.report.SignatureInformation; import eu.europa.ec.markt.dss.validation.report.SignatureLevelA; import eu.europa.ec.markt.dss.validation.report.SignatureLevelAnalysis; import eu.europa.ec.markt.dss.validation.report.SignatureLevelBES; import eu.europa.ec.markt.dss.validation.report.SignatureLevelC; import eu.europa.ec.markt.dss.validation.report.SignatureLevelEPES; import eu.europa.ec.markt.dss.validation.report.SignatureLevelLTV; import eu.europa.ec.markt.dss.validation.report.SignatureLevelT; import eu.europa.ec.markt.dss.validation.report.SignatureLevelX; import eu.europa.ec.markt.dss.validation.report.SignatureLevelXL; import eu.europa.ec.markt.dss.validation.report.SignatureVerification; import eu.europa.ec.markt.dss.validation.report.TimeInformation; import eu.europa.ec.markt.dss.validation.report.TimestampVerificationResult; import eu.europa.ec.markt.dss.validation.report.TrustedListInformation; import eu.europa.ec.markt.dss.validation.report.ValidationReport; import eu.europa.ec.markt.dss.validation.tsl.Condition; import eu.europa.ec.markt.dss.validation.tsl.PolicyIdCondition; import eu.europa.ec.markt.dss.validation.tsl.QcStatementCondition; import eu.europa.ec.markt.dss.validation.x509.TimestampToken; import eu.europa.ec.markt.dss.validation.xades.XMLDocumentValidator; import java.io.BufferedInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.security.NoSuchProviderException; import java.security.cert.CertificateEncodingException; import java.security.cert.X509CRL; import java.security.cert.X509Certificate; import java.util.ArrayList; import java.util.Arrays; import java.util.Date; import java.util.List; import java.util.logging.Level; import java.util.logging.Logger; import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream; 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.bouncycastle.ocsp.BasicOCSPResp; import org.xml.sax.SAXException; /** * Validate the signed document * * * @version $Revision: 1867 $ - $Date: 2013-04-08 13:44:56 +0200 (Mon, 08 Apr 2013) $ */ public abstract class SignedDocumentValidator { private static final Logger LOG = Logger.getLogger(SignedDocumentValidator.class.getName()); private static final String SVC_INFO = "http://uri.etsi.org/TrstSvc/eSigDir-1999-93-EC-TrustedList/SvcInfoExt/"; protected Document document; protected Document externalContent; private CertificateVerifier certificateVerifier; private Condition qcp = new PolicyIdCondition("0.4.0.1456.1.2"); private Condition qcpplus = new PolicyIdCondition("0.4.0.1456.1.1"); private Condition qccompliance = new QcStatementCondition(ETSIQCObjectIdentifiers.id_etsi_qcs_QcCompliance); private 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 SIGNATURES_XML = "META-INF/signatures.xml"; private static final String SIGNATURES_P7S = "META-INF/signatures.p7s"; /** * Guess the document format and return an appropriate document * * @param document * @return */ public static SignedDocumentValidator fromDocument(Document 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"); } catch (SAXException e) { throw new IOException("Not a valid XML"); } } 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[] xmlPreable = new byte[] { '<', '?', 'x', 'm', 'l' }; byte[] xmlUtf8 = new byte[] { -17, -69, -65, '<', '?' }; if (Arrays.equals(preamble, xmlPreable) || Arrays.equals(preamble, xmlUtf8)) { try { return new XMLDocumentValidator(document); } catch (ParserConfigurationException e) { throw new IOException("Not a valid XML"); } catch (SAXException e) { throw new IOException("Not a valid XML"); } } 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"); } } else { throw new RuntimeException("Document format not recognized/handled"); } } finally { if (input != null) { try { input.close(); } catch (IOException e) { } } } } private static SignedDocumentValidator getInstanceForAsics(Document document) throws IOException { ZipInputStream asics = new ZipInputStream(document.openStream()); try { ByteArrayOutputStream datafile = null; ByteArrayOutputStream signatures = null; ZipEntry entry; boolean cadesSigned = false; boolean xadesSigned = false; while ((entry = asics.getNextEntry()) != null) { if (entry.getName().equalsIgnoreCase(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().equalsIgnoreCase(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(); if (!Arrays.equals(mimetype.toByteArray(), MIMETYPE_ASIC_S.getBytes())) { throw new NotETSICompliantException(MSG.UNRECOGNIZED_TAG); } } else if (entry.getName().indexOf("/") == -1) { if (datafile == null) { datafile = new ByteArrayOutputStream(); IOUtils.copy(asics, datafile); datafile.close(); } else { throw new ProfileException("ASiC-S profile support only one data file"); } } } if (xadesSigned) { ASiCXMLDocumentValidator xmlValidator = new ASiCXMLDocumentValidator( new InMemoryDocument(signatures.toByteArray()), datafile.toByteArray()); 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) { } } } /** * Retrieves the signatures found in the document * * @return a list of AdvancedSignatures for validation purposes */ public abstract List<AdvancedSignature> getSignatures(); /** * Retrieves the number of signatures found in the document * * @return number of signatures */ public int numberOfSignatures() { List<AdvancedSignature> signatures = this.getSignatures(); if (signatures == null) { return 0; } return signatures.size(); } /** * @param certificateVerifier the certificateVerifier to set */ public void setCertificateVerifier(CertificateVerifier certificateVerifier) { this.certificateVerifier = certificateVerifier; } /** * Sets the Document containing the original content to sign, for detached signature scenarios * * @param externalContent the externalContent to set */ public void setExternalContent(Document externalContent) { this.externalContent = externalContent; } /** * @return the externalContent */ public Document getExternalContent() { return externalContent; } /** * @return the document */ public Document getDocument() { return document; } protected SignatureVerification[] verifyCounterSignatures(AdvancedSignature signature, ValidationContext ctx) { List<AdvancedSignature> counterSignatures = signature.getCounterSignatures(); if (counterSignatures == null) { return null; } List<SignatureVerification> counterSigVerifs = new ArrayList<SignatureVerification>(); for (AdvancedSignature counterSig : counterSignatures) { Result counterSigResult = new Result(counterSig.checkIntegrity(getExternalContent())); String counterSigAlg = counterSig.getSignatureAlgorithm(); counterSigVerifs.add(new SignatureVerification(counterSigResult, counterSigAlg)); } SignatureVerification[] ret = new SignatureVerification[counterSigVerifs.size()]; return counterSigVerifs.toArray(ret); } /** * Check the list of Timestamptoken. For each one a TimestampVerificationResult is produced * * @param signature * @param referenceTime * @param ctx * @param tstokens * @param data * @return */ protected List<TimestampVerificationResult> verifyTimestamps(AdvancedSignature signature, Date referenceTime, ValidationContext ctx, List<TimestampToken> tstokens, byte[] data) { List<TimestampVerificationResult> tstokenVerifs = new ArrayList<TimestampVerificationResult>(); if (tstokens != null) { for (TimestampToken t : tstokens) { TimestampVerificationResult verif = new TimestampVerificationResult(t); try { if (t.matchData(data)) { verif.setSameDigest(new Result(ResultStatus.VALID, null)); } else { verif.setSameDigest(new Result(ResultStatus.INVALID, "timestamp.dont.sign.data")); } } catch (NoSuchAlgorithmException ex) { /* We cannot verify the digest so the verification is "undetermined" */ verif.setSameDigest(new Result(ResultStatus.UNDETERMINED, "no.such.algoritm")); } /* Verify if there is a path up to the trusted list */ checkTimeStampCertPath(t, verif, ctx, signature); tstokenVerifs.add(verif); } } return tstokenVerifs; } protected SignatureLevelBES verifyLevelBES(AdvancedSignature signature, Date referenceTime, ValidationContext ctx) { try { Result signingCertRefVerification = new Result(); if (signature.getSigningCertificate() != null) { signingCertRefVerification.setStatus(ResultStatus.VALID, null); } else { signingCertRefVerification.setStatus(ResultStatus.INVALID, "no.signing.certificate"); } SignatureVerification[] counterSigsVerif = verifyCounterSignatures(signature, ctx); Result levelReached = new Result(signingCertRefVerification.isValid()); return new SignatureLevelBES(levelReached, signature, signingCertRefVerification, counterSigsVerif, null); } catch (Exception ex) { return new SignatureLevelBES(new Result(ResultStatus.INVALID, "exception.while.verifying"), null, new Result(ResultStatus.INVALID, "exception.while.verifying"), null, null); } } protected SignatureLevelEPES verifyLevelEPES(AdvancedSignature signature, Date referenceTime, ValidationContext ctx) { try { /* * We only check if a policy identifier is present. Actual signature policy validation is dependent on the * policy itself and therefore left to the user. */ PolicyValue policyValue = signature.getPolicyId(); Result levelReached = new Result(policyValue != null); return new SignatureLevelEPES(signature, levelReached); } catch (Exception ex) { return new SignatureLevelEPES(signature, new Result(ResultStatus.INVALID, "exception.while.verifying")); } } private Result resultForTimestamps(List<TimestampVerificationResult> signatureTimestampsVerification, Result levelReached) { if (signatureTimestampsVerification == null || signatureTimestampsVerification.isEmpty()) { levelReached.setStatus(ResultStatus.INVALID, "no.timestamp"); } else { levelReached.setStatus(ResultStatus.VALID, null); for (TimestampVerificationResult result : signatureTimestampsVerification) { if (result.getSameDigest().isUndetermined()) { levelReached.setStatus(ResultStatus.UNDETERMINED, "one.of.timestamp.digest.undetermined"); } else if (result.getSameDigest().isInvalid()) { levelReached.setStatus(ResultStatus.INVALID, "timestamp.dont.sign.data"); /* Not needed to continue */ break; } } } return levelReached; } protected SignatureLevelT verifyLevelT(AdvancedSignature signature, Date referenceTime, ValidationContext ctx) { List<TimestampToken> sigTimestamps = signature.getSignatureTimestamps(); List<TimestampVerificationResult> results = verifyTimestamps(signature, referenceTime, ctx, sigTimestamps, signature.getSignatureTimestampData()); return new SignatureLevelT(resultForTimestamps(results, new Result()), results); } private boolean everyCertificateRefAreThere(ValidationContext ctx, List<CertificateRef> refs, X509Certificate signingCert) { try { for (CertificateAndContext neededCert : ctx.getNeededCertificates()) { if (neededCert.getCertificate().equals(ctx.getCertificate())) { LOG.fine("Don't check for the signing certificate"); continue; } LOG.info("Looking for the CertificateRef of " + neededCert); boolean found = false; for (CertificateRef referencedCert : refs) { LOG.info("Compare to " + referencedCert); MessageDigest md = MessageDigest.getInstance(referencedCert.getDigestAlgorithm(), "BC"); byte[] hash = md.digest(neededCert.getCertificate().getEncoded()); if (Arrays.equals(hash, referencedCert.getDigestValue())) { found = true; break; } } LOG.info("Ref " + (found ? " found" : " not found")); if (!found) { return false; } } return true; } catch (NoSuchProviderException e) { /* * This should never happens. The provider BouncyCastle is supposed to be installed. No special treatment * for this exception */ throw new RuntimeException(e); } catch (CertificateEncodingException ex) { throw new RuntimeException(ex); } catch (NoSuchAlgorithmException e) { throw new RuntimeException(e); } } protected SignatureLevelC verifyLevelC(AdvancedSignature signature, Date referenceTime, ValidationContext ctx, boolean rehashValues) { try { List<CertificateRef> refs = signature.getCertificateRefs(); Result everyNeededCertAreInSignature = new Result(); if (refs == null || refs.isEmpty()) { everyNeededCertAreInSignature.setStatus(ResultStatus.INVALID, "no.certificate.ref"); } else { if (everyCertificateRefAreThere(ctx, refs, signature.getSigningCertificate())) { everyNeededCertAreInSignature.setStatus(ResultStatus.VALID, null); } else { everyNeededCertAreInSignature.setStatus(ResultStatus.INVALID, "not.all.needed.certificate.ref"); } } LOG.info("Every CertificateRef found " + everyNeededCertAreInSignature); List<OCSPRef> ocspRefs = signature.getOCSPRefs(); List<CRLRef> crlRefs = signature.getCRLRefs(); int refCount = 0; Result everyNeededRevocationData = new Result(ResultStatus.VALID, null); refCount += ocspRefs.size(); refCount += crlRefs.size(); Result thereIsRevocationData = null; Result levelCReached = null; if (rehashValues) { if (!everyOCSPValueOrRefAreThere(ctx, ocspRefs)) { everyNeededRevocationData.setStatus(ResultStatus.INVALID, "not.all.needed.ocsp.ref"); } if (!everyCRLValueOrRefAreThere(ctx, crlRefs)) { everyNeededRevocationData.setStatus(ResultStatus.INVALID, "not.all.needed.crl.ref"); } levelCReached = new Result(everyNeededCertAreInSignature.getStatus() == ResultStatus.VALID && everyNeededRevocationData.getStatus() == ResultStatus.VALID); return new SignatureLevelC(levelCReached, everyNeededCertAreInSignature, everyNeededRevocationData); } else { thereIsRevocationData = new Result(); if (refCount == 0) { thereIsRevocationData.setStatus(ResultStatus.INVALID, "no.revocation.data.reference"); } else { thereIsRevocationData.setStatus(ResultStatus.VALID, "at.least.one.reference"); } levelCReached = new Result(everyNeededCertAreInSignature.getStatus() == ResultStatus.VALID && thereIsRevocationData.getStatus() == ResultStatus.VALID); return new SignatureLevelC(levelCReached, everyNeededCertAreInSignature, thereIsRevocationData); } } catch (Exception ex) { return new SignatureLevelC(new Result(ResultStatus.INVALID, "exception.while.verifying"), new Result(ResultStatus.INVALID, "exception.while.verifying"), new Result(ResultStatus.INVALID, "exception.while.verifying")); } } private void checkTimeStampCertPath(TimestampToken t, TimestampVerificationResult result, ValidationContext ctx, AdvancedSignature signature) { try { /* Verify if there is a path up to the trusted list */ result.getCertPathUpToTrustedList().setStatus(ResultStatus.INVALID, "cannot.reached.tsl"); ctx.validateTimestamp(t, signature.getCertificateSource(), signature.getCRLSource(), signature.getOCSPSource()); for (CertificateAndContext c : ctx.getNeededCertificates()) { if (c.getCertificate().getSubjectX500Principal().equals(t.getSignerSubjectName())) { if (ctx.getParentFromTrustedList(c) != null) { result.getCertPathUpToTrustedList().setStatus(ResultStatus.VALID, null); break; } } } } catch (IOException ex) { result.getCertPathUpToTrustedList().setStatus(ResultStatus.UNDETERMINED, "exception.while.verifying"); } } protected SignatureLevelX verifyLevelX(AdvancedSignature signature, Date referenceTime, ValidationContext ctx) { try { Result levelReached = new Result(); levelReached.setStatus(ResultStatus.VALID, null); TimestampVerificationResult[] x1Results = null; TimestampVerificationResult[] x2Results = null; List<TimestampToken> timestampX1 = signature.getTimestampsX1(); if (timestampX1 != null && !timestampX1.isEmpty()) { byte[] data = signature.getTimestampX1Data(); x1Results = new TimestampVerificationResult[timestampX1.size()]; for (int i = 0; i < timestampX1.size(); i++) { try { TimestampToken t = timestampX1.get(i); x1Results[i] = new TimestampVerificationResult(t); if (!t.matchData(data)) { levelReached.setStatus(ResultStatus.INVALID, "timestamp.dont.sign.data"); x1Results[i] .setSameDigest(new Result(ResultStatus.INVALID, "timestamp.dont.sign.data")); } else { x1Results[i].setSameDigest(new Result(ResultStatus.VALID, null)); } /* Verify if there is a path up to the trusted list */ checkTimeStampCertPath(t, x1Results[i], ctx, signature); } catch (NoSuchAlgorithmException ex) { levelReached.setStatus(ResultStatus.UNDETERMINED, "no.such.algoritm"); } } } List<TimestampToken> timestampX2 = signature.getTimestampsX2(); if (timestampX2 != null && !timestampX2.isEmpty()) { byte[] data = signature.getTimestampX2Data(); x2Results = new TimestampVerificationResult[timestampX2.size()]; int i = 0; for (TimestampToken t : timestampX2) { try { x2Results[i] = new TimestampVerificationResult(t); if (!t.matchData(data)) { levelReached.setStatus(ResultStatus.INVALID, "timestamp.dont.sign.data"); x2Results[i] .setSameDigest(new Result(ResultStatus.INVALID, "timestamp.dont.sign.data")); } else { x2Results[i].setSameDigest(new Result(ResultStatus.VALID, null)); } /* Verify if there is a path up to the trusted list */ checkTimeStampCertPath(t, x2Results[i], ctx, signature); /* Verify if there is a path up to the trusted list */ } catch (NoSuchAlgorithmException ex) { levelReached.setStatus(ResultStatus.UNDETERMINED, "no.such.algoritm"); } } } if ((timestampX1 == null || timestampX1.isEmpty()) && (timestampX2 == null || timestampX2.isEmpty())) { levelReached.setStatus(ResultStatus.INVALID, "no.timestamp"); } return new SignatureLevelX(signature, levelReached, x1Results, x2Results); } catch (Exception ex) { return new SignatureLevelX(signature, new Result(ResultStatus.INVALID, "exception.while.verifying")); } } /** * For level -XL, every certificates values contained in the ValidationContext (except the SigningCertificate) must * be in the CertificatesValues of the signature * * @param ctx * @param certificates * @param signingCert * @return */ protected boolean everyCertificateValueAreThere(ValidationContext ctx, List<X509Certificate> certificates, X509Certificate signingCert) { for (CertificateAndContext neededCert : ctx.getNeededCertificates()) { /* We don't need the signing certificate in the XL values */ if (neededCert.getCertificate().equals(signingCert)) { continue; } LOG.info("Looking for the certificate ref of " + neededCert); boolean found = false; for (X509Certificate referencedCert : certificates) { LOG.info("Compare to " + referencedCert.getSubjectDN()); if (referencedCert.equals(neededCert.getCertificate())) { found = true; break; } } LOG.info("Cert " + (found ? " found" : " not found")); if (!found) { return false; } } return true; } /** * For level -XL or C, every BasicOCSPResponse values contained in the ValidationContext must be in the * RevocationValues or the RevocationRef of the signature * * @param ctx * @param refs * @param signingCert * @return */ protected boolean everyOCSPValueOrRefAreThere(ValidationContext ctx, List<?> ocspValuesOrRef) { for (BasicOCSPResp ocspResp : ctx.getNeededOCSPResp()) { LOG.info("Looking for the OCSPResp produced at " + ocspResp.getProducedAt()); boolean found = false; for (Object valueOrRef : ocspValuesOrRef) { if (valueOrRef instanceof BasicOCSPResp) { BasicOCSPResp sigResp = (BasicOCSPResp) valueOrRef; if (sigResp.equals(ocspResp)) { found = true; break; } } if (valueOrRef instanceof OCSPRef) { OCSPRef ref = (OCSPRef) valueOrRef; if (ref.match(ocspResp)) { found = true; break; } } } LOG.info("Ref " + (found ? " found" : " not found")); if (!found) { return false; } } return true; } /** * For level -XL, every X509CRL values contained in the ValidationContext must be in the RevocationValues of the * signature * * @param ctx * @param refs * @param signingCert * @return */ protected boolean everyCRLValueOrRefAreThere(ValidationContext ctx, List<?> crlValuesOrRef) { for (X509CRL crl : ctx.getNeededCRL()) { LOG.info("Looking for CRL ref issued by " + crl.getIssuerX500Principal()); boolean found = false; for (Object valueOrRef : crlValuesOrRef) { if (valueOrRef instanceof X509CRL) { X509CRL sigCRL = (X509CRL) valueOrRef; if (sigCRL.equals(crl)) { found = true; break; } } if (valueOrRef instanceof CRLRef) { CRLRef ref = (CRLRef) valueOrRef; if (ref.match(crl)) { found = true; break; } } } LOG.info("Ref " + (found ? " found" : " not found")); if (!found) { return false; } } return true; } protected SignatureLevelXL verifyLevelXL(AdvancedSignature signature, Date referenceTime, ValidationContext ctx) { try { Result levelReached = new Result(); Result everyNeededCertAreInSignature = new Result(); everyNeededCertAreInSignature.setStatus(ResultStatus.VALID, null); Result everyNeededRevocationData = new Result(); everyNeededRevocationData.setStatus(ResultStatus.VALID, null); List<X509Certificate> refs = signature.getCertificates(); if (refs.isEmpty()) { LOG.info("There is no certificate refs in the signature"); everyNeededCertAreInSignature.setStatus(ResultStatus.INVALID, "no.certificate.value"); } else { if (!everyCertificateValueAreThere(ctx, refs, signature.getSigningCertificate())) { everyNeededCertAreInSignature.setStatus(ResultStatus.INVALID, "not.all.needed.certificate.value"); } } LOG.info("Every certificate found " + everyNeededCertAreInSignature); /* Count of revocation values in the -XL signature */ int valueCount = 0; List<BasicOCSPResp> ocspValues = signature.getOCSPs(); if (ocspValues != null) { valueCount += ocspValues.size(); if (!everyOCSPValueOrRefAreThere(ctx, ocspValues)) { everyNeededRevocationData.setStatus(ResultStatus.INVALID, "not.all.needed.ocsp.value"); } } List<X509CRL> crlValues = signature.getCRLs(); if (crlValues != null) { valueCount += crlValues.size(); if (!everyCRLValueOrRefAreThere(ctx, crlValues)) { everyNeededRevocationData.setStatus(ResultStatus.INVALID, "not.all.needed.crl.value"); } } /* If there is no revocation value in the -XL signature, the signature is invalid */ if (valueCount == 0) { everyNeededRevocationData.setStatus(ResultStatus.INVALID, "no.revocation.data.value"); } levelReached.setStatus((everyNeededCertAreInSignature.getStatus() == ResultStatus.VALID && everyNeededRevocationData.getStatus() == ResultStatus.VALID) ? ResultStatus.VALID : ResultStatus.INVALID, null); return new SignatureLevelXL(levelReached, everyNeededCertAreInSignature, everyNeededRevocationData); } catch (Exception ex) { return new SignatureLevelXL(new Result(ResultStatus.INVALID, "exception.while.verifying"), new Result(ResultStatus.INVALID, "exception.while.verifying"), new Result(ResultStatus.INVALID, "exception.while.verifying")); } } protected SignatureLevelA verifyLevelA(AdvancedSignature signature, Date referenceTime, ValidationContext ctx) { try { Result levelReached = new Result(); List<TimestampVerificationResult> verifs = null; try { List<TimestampToken> timestamps = signature.getArchiveTimestamps(); verifs = verifyTimestamps(signature, referenceTime, ctx, timestamps, signature.getArchiveTimestampData(0, externalContent)); } catch (IOException e) { LOG.log(Level.SEVERE, "Error verifyind level A", e); levelReached.setStatus(ResultStatus.UNDETERMINED, "exception.while.verifying"); } return new SignatureLevelA(resultForTimestamps(verifs, levelReached), verifs); } catch (Exception ex) { return new SignatureLevelA(new Result(ResultStatus.INVALID, "exception.while.verifying"), null); } } protected SignatureLevelLTV verifyLevelLTV(AdvancedSignature signature, Date referenceTime, ValidationContext ctx) { return null; } protected QualificationsVerification verifyQualificationsElement(AdvancedSignature signature, Date referenceTime, ValidationContext ctx) { Result qCWithSSCD = new Result(); Result qCNoSSCD = new Result(); Result qCSSCDStatusAsInCert = new Result(); Result qCForLegalPerson = new Result(); List<String> qualifiers = ctx.getQualificationStatement(); if (qualifiers != null) { qCWithSSCD = new Result(qualifiers.contains(SVC_INFO + "QCWithSSCD")); qCNoSSCD = new Result(qualifiers.contains(SVC_INFO + "QCNoSSCD")); qCSSCDStatusAsInCert = new Result(qualifiers.contains(SVC_INFO + "QCSSCDStatusAsInCert")); qCForLegalPerson = new Result(qualifiers.contains(SVC_INFO + "QCForLegalPerson")); } return new QualificationsVerification(qCWithSSCD, qCNoSSCD, qCSSCDStatusAsInCert, qCForLegalPerson); } protected QCStatementInformation verifyQStatement(X509Certificate certificate) { if (certificate != null) { Result qCPPresent = new Result(qcp.check(new CertificateAndContext(certificate))); Result qCPPlusPresent = new Result(qcpplus.check(new CertificateAndContext(certificate))); Result qcCompliancePresent = new Result(qccompliance.check(new CertificateAndContext(certificate))); Result qcSCCDPresent = new Result(qcsscd.check(new CertificateAndContext(certificate))); return new QCStatementInformation(qCPPresent, qCPPlusPresent, qcCompliancePresent, qcSCCDPresent); } else { return new QCStatementInformation(null, null, null, null); } } /** * Main method for validating a signature * * @param signature * @param referenceTime * @return the report part pertaining to the signature */ protected SignatureInformation validateSignature(AdvancedSignature signature, Date referenceTime) { if (signature.getSigningCertificate() == null) { LOG.severe("There is no signing certificate"); return null; } QCStatementInformation qcStatementInformation = verifyQStatement(signature.getSigningCertificate()); SignatureVerification signatureVerification = new SignatureVerification( new Result(signature.checkIntegrity(this.externalContent)), signature.getSignatureAlgorithm()); try { ValidationContext ctx = certificateVerifier.validateCertificate(signature.getSigningCertificate(), referenceTime, signature.getCertificateSource(), signature.getCRLSource(), signature.getOCSPSource()); TrustedListInformation info = new TrustedListInformation(ctx.getRelevantServiceInfo()); CertPathRevocationAnalysis path = new CertPathRevocationAnalysis(ctx, info); /* * We first check the level XL, because we want to know if it's possible to check the RevocationDataRef or * not */ SignatureLevelXL signatureLevelXL = verifyLevelXL(signature, referenceTime, ctx); /* If level XL is reached, then it's possible to rehash the values */ SignatureLevelC signatureLevelC = verifyLevelC(signature, referenceTime, ctx, signatureLevelXL != null ? signatureLevelXL.getLevelReached().isValid() : false); SignatureLevelAnalysis signatureLevelAnalysis = new SignatureLevelAnalysis(signature, verifyLevelBES(signature, referenceTime, ctx), verifyLevelEPES(signature, referenceTime, ctx), verifyLevelT(signature, referenceTime, ctx), signatureLevelC, verifyLevelX(signature, referenceTime, ctx), signatureLevelXL, verifyLevelA(signature, referenceTime, ctx), verifyLevelLTV(signature, referenceTime, ctx)); QualificationsVerification qualificationsVerification = verifyQualificationsElement(signature, referenceTime, ctx); SignatureInformation signatureInformation = new SignatureInformation(signatureVerification, path, signatureLevelAnalysis, qualificationsVerification, qcStatementInformation); return signatureInformation; } catch (IOException e) { throw new RuntimeException("Cannot read signature file", e); } } /** * Validate the document and all its signatures * * @return the validation report */ public ValidationReport validateDocument() { Date verificationTime = new Date(); TimeInformation timeInformation = new TimeInformation(verificationTime); /* Create a report for each signature */ List<SignatureInformation> signatureInformationList = new ArrayList<SignatureInformation>(); for (AdvancedSignature signature : getSignatures()) { signatureInformationList.add(validateSignature(signature, signature.getSigningTime() == null ? new Date() : signature.getSigningTime())); } return new ValidationReport(timeInformation, signatureInformationList); } }