package eu.europa.esig.dss.validation.process.vpfswatsp.checks.vts; import java.util.Collections; import java.util.Date; import java.util.List; import java.util.Set; import eu.europa.esig.dss.jaxb.detailedreport.XmlRFC; import eu.europa.esig.dss.jaxb.detailedreport.XmlVTS; import eu.europa.esig.dss.utils.Utils; import eu.europa.esig.dss.validation.TimestampReferenceCategory; import eu.europa.esig.dss.validation.policy.Context; import eu.europa.esig.dss.validation.policy.SubContext; import eu.europa.esig.dss.validation.policy.ValidationPolicy; import eu.europa.esig.dss.validation.policy.rules.Indication; import eu.europa.esig.dss.validation.process.Chain; import eu.europa.esig.dss.validation.process.ChainItem; import eu.europa.esig.dss.validation.process.bbb.sav.checks.CryptographicCheck; import eu.europa.esig.dss.validation.process.bbb.xcv.rfc.RevocationFreshnessChecker; import eu.europa.esig.dss.validation.process.vpfswatsp.POEExtraction; import eu.europa.esig.dss.validation.process.vpfswatsp.checks.vts.checks.POEExistsAtOrBeforeControlTimeCheck; import eu.europa.esig.dss.validation.process.vpfswatsp.checks.vts.checks.SatisfyingRevocationDataExistsCheck; import eu.europa.esig.dss.validation.reports.wrapper.CertificateWrapper; import eu.europa.esig.dss.validation.reports.wrapper.DiagnosticData; import eu.europa.esig.dss.validation.reports.wrapper.RevocationWrapper; import eu.europa.esig.dss.validation.reports.wrapper.TokenProxy; import eu.europa.esig.jaxb.policy.CryptographicConstraint; public class ValidationTimeSliding extends Chain<XmlVTS> { private final DiagnosticData diagnosticData; private final TokenProxy token; private final Date currentTime; private final Context context; private final POEExtraction poe; private final ValidationPolicy policy; private Date controlTime; public ValidationTimeSliding(DiagnosticData diagnosticData, TokenProxy token, Date currentTime, Context context, POEExtraction poe, ValidationPolicy policy) { super(new XmlVTS()); this.diagnosticData = diagnosticData; this.token = token; this.currentTime = currentTime; this.context = context; this.poe = poe; this.policy = policy; } @Override protected void initChain() { /* * 5.6.2.2.4 Processing * * 1) The building block shall initialize control-time to the current * date/time. * * NOTE 1: Control-time is an internal variable that is used within the * algorithms and not part of the core results of the validation * process. */ controlTime = currentTime; List<String> certificateChainIds = token.getCertificateChainIds(); if (Utils.isCollectionNotEmpty(certificateChainIds)) { /* * 2) For each certificate in the chain starting from the first * certificate (the certificate issued by the trust anchor): */ Collections.reverse(certificateChainIds); // trusted_list -> ... -> // signature for (String certificateId : certificateChainIds) { CertificateWrapper certificate = diagnosticData.getUsedCertificateById(certificateId); if (certificate.isTrusted()) { continue; } /* * a) The building block shall find revocation status * information satisfying the following: * * - The revocation status information is consistent with the * rules conditioning its use to check the revocation status of * the considered certificate. In the case of a CRL, it shall * satisfy the checks specified in IETF RFC 5280 [1] clause 6.3; * and * * - The issuance date of the revocation status information is * before control-time. If more than one revocation status is * found, the building block shall consider the most recent one * and shall go to the next step. * * If more than one revocation status is found, the building block shall consider the most recent one * and shall go to the next step. * * If there is no such information, The building block shall * return the indication INDETERMINATE with the sub-indication * NO_POE. */ RevocationWrapper latestCompliantRevocation = null; Set<RevocationWrapper> revocations = certificate.getRevocationData(); for (RevocationWrapper revocation : revocations) { if ((latestCompliantRevocation == null || revocation.getProductionDate().after(latestCompliantRevocation.getProductionDate())) && isConsistant(certificate, revocation) && isIssuanceBeforeControlTime(revocation)) { latestCompliantRevocation = revocation; } } ChainItem<XmlVTS> item = satisfyingRevocationDataExists(latestCompliantRevocation); if (firstItem == null) { firstItem = item; } /* * b) If the set of POEs contains a proof of existence * of the certificate and the revocation status * information at (or before) control-time, the building * block shall go to step c). * * Otherwise, the building block shall return the * indication INDETERMINATE with the sub-indication * NO_POE. */ item = item.setNextItem(poeExistsAtOrBeforeControlTime(certificate, TimestampReferenceCategory.CERTIFICATE, controlTime)); item = item.setNextItem(poeExistsAtOrBeforeControlTime(latestCompliantRevocation, TimestampReferenceCategory.REVOCATION, controlTime)); /* * c) The update of the value of control-time is as * follows: * * - If the certificate is marked as revoked in the * revocation status information, the building block * shall set control-time to the revocation time. * * - If the certificate is not marked as revoked, the * building block shall run the Revocation Freshness * Checker with the used revocation information status, * the certificate for which the revocation status is * being checked and the control-time. If it returns * FAILED, the building block shall set control-time to * the issuance time of the revocation status * information. * * Otherwise, the building block shall not change the * value of control-time. */ if (latestCompliantRevocation != null) { if (certificate.isRevoked()) { controlTime = latestCompliantRevocation.getRevocationDate(); } else if (!isFresh(latestCompliantRevocation, controlTime)) { controlTime = latestCompliantRevocation.getProductionDate(); } } /* * d) The building block shall apply the cryptographic * constraints to the certificate and the revocation * status information against the control-time. If the * certificate (or the revocation status information) * does not match these constraints, the building block * shall set control-time to the lowest time up to which * the listed algorithms were considered reliable. */ item = item.setNextItem(cryptographicCheck(certificate, controlTime)); item = item.setNextItem(cryptographicCheck(latestCompliantRevocation, controlTime)); } } } @Override protected void addAdditionalInfo() { result.setControlTime(controlTime); } private boolean isFresh(RevocationWrapper revocationData, Date controlTime) { // TODO SubContext ?? RevocationFreshnessChecker rfc = new RevocationFreshnessChecker(revocationData, controlTime, context, SubContext.SIGNING_CERT, policy); XmlRFC execute = rfc.execute(); return execute != null && execute.getConclusion() != null && Indication.PASSED.equals(execute.getConclusion().getIndication()); } private ChainItem<XmlVTS> satisfyingRevocationDataExists(RevocationWrapper revocationData) { return new SatisfyingRevocationDataExistsCheck(result, revocationData, getFailLevelConstraint()); } private ChainItem<XmlVTS> poeExistsAtOrBeforeControlTime(TokenProxy token, TimestampReferenceCategory referenceCategory, Date controlTime) { return new POEExistsAtOrBeforeControlTimeCheck(result, token, referenceCategory, controlTime, poe, getFailLevelConstraint()); } private ChainItem<XmlVTS> cryptographicCheck(TokenProxy token, Date validationTime) { CryptographicConstraint constraint = policy.getCertificateCryptographicConstraint(context, SubContext.SIGNING_CERT); return new CryptographicCheck<XmlVTS>(result, token, validationTime, constraint); } private boolean isConsistant(CertificateWrapper certificate, RevocationWrapper revocationData) { Date certNotBefore = certificate.getNotBefore(); Date certNotAfter = certificate.getNotAfter(); Date thisUpdate = revocationData.getThisUpdate(); Date notAfterRevoc = thisUpdate; /* * If a CRL contains the extension expiredCertsOnCRL defined in [i.12], it shall prevail over the TL * extension value but only for that specific CRL. */ Date expiredCertsOnCRL = revocationData.getExpiredCertsOnCRL(); if (expiredCertsOnCRL != null) { notAfterRevoc = expiredCertsOnCRL; } /* * If an OCSP response contains the extension ArchiveCutoff defined in section 4.4.4 of * IETF RFC 6960 [i.11], it shall prevail over the TL extension value but only for that specific OCSP * response. */ Date archiveCutOff = revocationData.getArchiveCutOff(); if (archiveCutOff != null) { notAfterRevoc = archiveCutOff; } /* expiredCertsRevocationInfo Extension from TL */ if (expiredCertsOnCRL != null && archiveCutOff != null) { String revocationSigningCertificateId = revocationData.getSigningCertificateId(); CertificateWrapper revocCert = diagnosticData.getUsedCertificateById(revocationSigningCertificateId); if (revocCert != null) { Date expiredCertsRevocationInfo = revocCert.getCertificateTSPServiceExpiredCertsRevocationInfo(); if (expiredCertsRevocationInfo != null) { notAfterRevoc = expiredCertsRevocationInfo; } } } return thisUpdate != null && certNotBefore.before(thisUpdate) && (certNotAfter.compareTo(notAfterRevoc) >= 0); } private boolean isIssuanceBeforeControlTime(RevocationWrapper revocationData) { Date issuanceDate = revocationData.getProductionDate(); return issuanceDate.before(controlTime); } }