package eu.europa.esig.dss.validation.process.vpfbs.checks;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import eu.europa.esig.dss.jaxb.detailedreport.XmlBasicBuildingBlocks;
import eu.europa.esig.dss.jaxb.detailedreport.XmlCV;
import eu.europa.esig.dss.jaxb.detailedreport.XmlConclusion;
import eu.europa.esig.dss.jaxb.detailedreport.XmlFC;
import eu.europa.esig.dss.jaxb.detailedreport.XmlISC;
import eu.europa.esig.dss.jaxb.detailedreport.XmlName;
import eu.europa.esig.dss.jaxb.detailedreport.XmlSAV;
import eu.europa.esig.dss.jaxb.detailedreport.XmlVCI;
import eu.europa.esig.dss.jaxb.detailedreport.XmlValidationProcessBasicSignatures;
import eu.europa.esig.dss.jaxb.detailedreport.XmlXCV;
import eu.europa.esig.dss.utils.Utils;
import eu.europa.esig.dss.validation.policy.rules.Indication;
import eu.europa.esig.dss.validation.policy.rules.SubIndication;
import eu.europa.esig.dss.validation.process.ChainItem;
import eu.europa.esig.dss.validation.process.MessageTag;
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.SignatureWrapper;
import eu.europa.esig.dss.validation.reports.wrapper.TimestampWrapper;
import eu.europa.esig.dss.x509.TimestampType;
import eu.europa.esig.jaxb.policy.LevelConstraint;
public class SignatureBasicBuildingBlocksCheck extends ChainItem<XmlValidationProcessBasicSignatures> {
private final DiagnosticData diagnosticData;
private final XmlBasicBuildingBlocks signatureBBB;
private final Map<String, XmlBasicBuildingBlocks> bbbs;
private Indication indication;
private SubIndication subIndication;
private List<XmlName> errors = new ArrayList<XmlName>();
public SignatureBasicBuildingBlocksCheck(XmlValidationProcessBasicSignatures result, DiagnosticData diagnosticData, XmlBasicBuildingBlocks signatureBBB,
Map<String, XmlBasicBuildingBlocks> bbbs, LevelConstraint constraint) {
super(result, constraint, signatureBBB.getId());
this.diagnosticData = diagnosticData;
this.signatureBBB = signatureBBB;
this.bbbs = bbbs;
}
@Override
protected boolean process() {
/*
* 1) Token signature validation: the building block shall perform the validation process for Basic Signatures
* as per clause 5.3 with the time-stamp token. In all the steps of this process, the building block shall take
* into account that the signature to validate is a time-stamp token (e.g. to select TSA trust-anchors). If this
* step
* returns PASSED, the building block shall go to the next step. Otherwise, the building block shall return the
* indication and information returned by the validation process.
*/
XmlFC fc = signatureBBB.getFC();
if (fc != null) {
XmlConclusion fcConclusion = fc.getConclusion();
if (!Indication.PASSED.equals(fcConclusion.getIndication())) {
indication = fcConclusion.getIndication();
subIndication = fcConclusion.getSubIndication();
errors.addAll(fcConclusion.getErrors());
return false;
}
}
/*
* 5.3.4 2) The Basic Signature validation process shall perform the identification of the signing certificate
* (as per clause 5.2.3) with the signature and the signing certificate, if provided as a parameter.
*
* If the identification of the signing certificate process returns the indication INDETERMINATE with the
* sub-indication NO_SIGNING_CERTIFICATE_FOUND, the Basic Signature validation process shall return the
* indication INDETERMINATE with the sub-indication NO_SIGNING_CERTIFICATE_FOUND,
*
* otherwise it shall go to the next step.
*/
XmlISC isc = signatureBBB.getISC();
XmlConclusion iscConclusion = isc.getConclusion();
if (Indication.INDETERMINATE.equals(iscConclusion.getIndication())
&& SubIndication.NO_SIGNING_CERTIFICATE_FOUND.equals(iscConclusion.getSubIndication())) {
indication = iscConclusion.getIndication();
subIndication = iscConclusion.getSubIndication();
errors.addAll(iscConclusion.getErrors());
return false;
}
/*
* 5.3.4 3) The Basic Signature validation process shall perform the Validation Context Initialization as per
* clause 5.2.4.
*
* If the process returns INDETERMINATE with some sub-indication, return with the indication INDETERMINATE
* together with that sub-indication,
*
* otherwise go to the next step.
*/
XmlVCI vci = signatureBBB.getVCI();
if (vci != null) {
XmlConclusion vciConclusion = vci.getConclusion();
if (Indication.INDETERMINATE.equals(vciConclusion.getIndication())) {
indication = vciConclusion.getIndication();
subIndication = vciConclusion.getSubIndication();
errors.addAll(vciConclusion.getErrors());
return false;
}
}
/*
* 5.3.4 4) The Basic Signature validation process shall perform the Cryptographic Verification process as per
* clause 5.2.7 with the following inputs:
*
* a) The signature.
* b) The certificate chain returned in the previous step. And
* c) The signed data object(s).
*
* If the cryptographic signature validation process returns PASSED, the Basic Signature validation process
* shall go to the next step.
*
* Otherwise, the Basic Signature validation process shall return the returned indication, sub-indication and
* associated information provided by the cryptographic signature validation process.
*/
XmlCV cv = signatureBBB.getCV();
XmlConclusion cvConclusion = cv.getConclusion();
if (!Indication.PASSED.equals(cvConclusion.getIndication())) {
indication = cvConclusion.getIndication();
subIndication = cvConclusion.getSubIndication();
errors.addAll(cvConclusion.getErrors());
return false;
}
/*
* 5.3.4 5) The Basic Signature validation process shall perform the X.509 Certificate Validation as per clause
* 5.2.6 with the following inputs:
*
* a) The signing certificate obtained in step 1. And
* b) X.509 validation constraints, certificate validation-data, chain constraints and cryptographic constraints
* obtained in step 3 or provided as input.
*
* If the signing certificate validation process returns the indication PASSED, the Basic Signature validation
* process shall go to the next step.
*
* If the signing certificate validation process returns the indication INDETERMINATE with the sub-indication
* REVOKED_NO_POE and if the signature contains a content-time-stamp attribute, the Basic Signature validation
* process shall perform the validation process for AdES time-stamps as defined in clause 5.4. If this process
* returns the indication PASSED and the generation time of the time-stamp token is after the revocation time,
* the Basic Signature validation process shall return the indication FAILED with the sub-indication REVOKED. In
* all other cases, the Basic Signature validation process shall return the indication INDETERMINATE with the
* sub-indication REVOKED_NO_POE.
*
* If the signing certificate validation process returns the indication INDETERMINATE with the sub-indication
* OUT_OF_BOUNDS_NO_POE and if the signature contains a content-time-stamp attribute, the Basic Signature
* validation process shall perform the validation process for AdES time-stamps as defined in clause 5.4. If it
* returns the indication PASSED and the generation time of the time-stamp token is after the expiration date of
* the signing certificate, the Basic Signature validation process shall return the indication INDETERMINATE
* with the sub-indication EXPIRED. Otherwise, the Basic Signature validation process shall return the
* indication INDETERMINATE with the sub-indication OUT_OF_BOUNDS_NO_POE.
*
* In all other cases, the Basic Signature validation process shall return the indication, sub-indication and
* any associated information returned by the signing certificate validation process.
*/
XmlXCV xcv = signatureBBB.getXCV();
XmlConclusion xcvConclusion = xcv.getConclusion();
if (Indication.INDETERMINATE.equals(xcvConclusion.getIndication()) && SubIndication.REVOKED_NO_POE.equals(xcvConclusion.getSubIndication())) {
SignatureWrapper currentSignature = diagnosticData.getSignatureById(signatureBBB.getId());
List<TimestampWrapper> contentTimestamps = currentSignature.getTimestampListByType(TimestampType.CONTENT_TIMESTAMP);
if (Utils.isCollectionNotEmpty(contentTimestamps)) {
boolean failed = false;
Date revocationDate = getRevocationDateForSigningCertificate(currentSignature);
for (TimestampWrapper timestamp : contentTimestamps) {
if (isValidTimestamp(timestamp)) {
Date tspProductionTime = timestamp.getProductionTime();
if (tspProductionTime.after(revocationDate)) {
failed = true;
break;
}
}
}
if (failed) {
indication = Indication.FAILED;
subIndication = SubIndication.REVOKED;
errors.addAll(xcvConclusion.getErrors());
return false;
}
}
indication = Indication.INDETERMINATE;
subIndication = SubIndication.REVOKED_NO_POE;
errors.addAll(xcvConclusion.getErrors());
return false;
} else if (Indication.INDETERMINATE.equals(xcvConclusion.getIndication())
&& SubIndication.OUT_OF_BOUNDS_NO_POE.equals(xcvConclusion.getSubIndication())) {
SignatureWrapper currentSignature = diagnosticData.getSignatureById(signatureBBB.getId());
List<TimestampWrapper> contentTimestamps = currentSignature.getTimestampListByType(TimestampType.CONTENT_TIMESTAMP);
if (Utils.isCollectionNotEmpty(contentTimestamps)) {
boolean failed = false;
Date expirationDate = getExpirationDateForSigningCertificate(currentSignature);
for (TimestampWrapper timestamp : contentTimestamps) {
if (isValidTimestamp(timestamp)) {
Date tspProductionTime = timestamp.getProductionTime();
if (tspProductionTime.after(expirationDate)) {
failed = true;
break;
}
}
}
if (failed) {
indication = Indication.INDETERMINATE;
subIndication = SubIndication.EXPIRED;
errors.addAll(xcvConclusion.getErrors());
return false;
}
}
indication = Indication.INDETERMINATE;
subIndication = SubIndication.OUT_OF_BOUNDS_NO_POE;
errors.addAll(xcvConclusion.getErrors());
return false;
} else if (!Indication.PASSED.equals(xcvConclusion.getIndication())) {
indication = xcvConclusion.getIndication();
subIndication = xcvConclusion.getSubIndication();
errors.addAll(xcvConclusion.getErrors());
return false;
}
/*
* 5.3.4 6) The Basic Signature validation process shall perform the Signature Acceptance Validation process as
* per clause 5.2.8 with the following inputs:
* a) The signature.
* b) The Cryptographic Constraints. And
* c) The Signature Elements Constraints.
*
* If the signature acceptance validation process returns PASSED, the Basic Signature validation process shall
* go to the next step.
*
* If the signature acceptance validation process returns the indication INDETERMINATE with the sub-indication
* CRYPTO_CONSTRAINTS_FAILURE_NO_POE and the material concerned by this failure is the signature value and if
* the signature contains a content-time-stamp attribute, the Basic Signature validation process shall perform
* the validation process for AdES time-stamps as defined in clause 5.4. If it returns the indication PASSED and
* the algorithm(s) concerned were no longer considered reliable at the generation time of the timestamp token,
* the Basic Signature validation process shall return the indication FAILED with the sub-indication
* CRYPTO_CONSTRAINTS_FAILURE. In all other cases, the Basic Signature validation process shall return the
* indication INDETERMINATE with the sub-indication CRYPTO_CONSTRAINTS_FAILURE_NO_POE.
*
* NOTE 2: The content time-stamp is a signed attribute and hence proves that the signature value was produced
* after the generation time of the time-stamp token.
* NOTE 3: In case this clause returns INDETERMINATE/CRYPTO_CONSTRAINTS_FAILURE_NO_POE, the validation process
* for signature with long-term validation data and with archival data can be used to validate the signature, if
* other POE (e.g. from a trusted archive) exist.
*
* In all other cases, the Basic Signature validation process shall return the indication and associated
* information returned by the signature acceptance validation building block.
*/
XmlSAV sav = signatureBBB.getSAV();
XmlConclusion savConclusion = sav.getConclusion();
if (Indication.INDETERMINATE.equals(savConclusion.getIndication())
&& SubIndication.CRYPTO_CONSTRAINTS_FAILURE_NO_POE.equals(savConclusion.getSubIndication())) {
SignatureWrapper currentSignature = diagnosticData.getSignatureById(signatureBBB.getId());
List<TimestampWrapper> contentTimestamps = currentSignature.getTimestampListByType(TimestampType.CONTENT_TIMESTAMP);
if (Utils.isCollectionNotEmpty(contentTimestamps)) {
boolean failed = false;
for (TimestampWrapper timestamp : contentTimestamps) {
if (isValidTimestamp(timestamp)) {
failed = true;
break;
}
}
if (failed) {
indication = Indication.FAILED;
subIndication = SubIndication.CRYPTO_CONSTRAINTS_FAILURE;
return false;
}
}
} else if (!Indication.PASSED.equals(savConclusion.getIndication())) {
indication = savConclusion.getIndication();
subIndication = savConclusion.getSubIndication();
errors.addAll(savConclusion.getErrors());
return false;
}
return true;
}
private boolean isValidTimestamp(TimestampWrapper timestamp) {
XmlBasicBuildingBlocks timestampBasicBuildingBlocks = bbbs.get(timestamp.getId());
return (timestampBasicBuildingBlocks != null && timestampBasicBuildingBlocks.getConclusion() != null)
&& Indication.PASSED.equals(timestampBasicBuildingBlocks.getConclusion().getIndication());
}
private Date getRevocationDateForSigningCertificate(SignatureWrapper currentSignature) {
CertificateWrapper signingCertificate = diagnosticData.getUsedCertificateById(currentSignature.getSigningCertificateId());
if (signingCertificate != null && signingCertificate.getRevocationData() != null) {
return signingCertificate.getLatestRevocationData().getRevocationDate();
}
return null;
}
private Date getExpirationDateForSigningCertificate(SignatureWrapper currentSignature) {
CertificateWrapper signingCertificate = diagnosticData.getUsedCertificateById(currentSignature.getSigningCertificateId());
if (signingCertificate != null) {
return signingCertificate.getNotAfter();
}
return null;
}
@Override
protected MessageTag getMessageTag() {
return MessageTag.ADEST_ROBVPIIC;
}
@Override
protected MessageTag getErrorMessageTag() {
return MessageTag.ADEST_ROBVPIIC_ANS;
}
@Override
protected Indication getFailedIndicationForConclusion() {
return indication;
}
@Override
protected SubIndication getFailedSubIndicationForConclusion() {
return subIndication;
}
@Override
protected List<XmlName> getPreviousErrors() {
return errors;
}
}