Java tutorial
/** * DSS - Digital Signature Services * Copyright (C) 2015 European Commission, provided under the CEF programme * * This file is part of the "DSS - Digital Signature Services" project. * * This library 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. * * This library 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 this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ package eu.europa.esig.dss.validation.process; import java.util.ArrayList; import java.util.Date; import java.util.List; import org.apache.commons.lang.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import eu.europa.esig.dss.DSSException; import eu.europa.esig.dss.DSSUtils; import eu.europa.esig.dss.XmlDom; import eu.europa.esig.dss.validation.policy.BasicValidationProcessValidConstraint; import eu.europa.esig.dss.validation.policy.Constraint; import eu.europa.esig.dss.validation.policy.ProcessParameters; import eu.europa.esig.dss.validation.policy.RuleUtils; import eu.europa.esig.dss.validation.policy.TimestampValidationProcessValidConstraint; import eu.europa.esig.dss.validation.policy.ValidationPolicy; import eu.europa.esig.dss.validation.policy.XmlNode; import eu.europa.esig.dss.validation.policy.rules.AttributeName; import eu.europa.esig.dss.validation.policy.rules.AttributeValue; import eu.europa.esig.dss.validation.policy.rules.ExceptionMessage; import eu.europa.esig.dss.validation.policy.rules.Indication; import eu.europa.esig.dss.validation.policy.rules.MessageTag; import eu.europa.esig.dss.validation.policy.rules.NodeName; import eu.europa.esig.dss.validation.policy.rules.NodeValue; import eu.europa.esig.dss.validation.policy.rules.SubIndication; import eu.europa.esig.dss.validation.report.Conclusion; import eu.europa.esig.dss.x509.TimestampType; /** * This class implements:<br> * * 8 Validation Process for AdES-T * * 8.1 Description<br> * * An AdES-T signature is built on BES or EPES signature and incorporates trusted time associated to the signature. The * trusted time may be provided by two different means: * * A signature time-stamp unsigned property/attribute added to the electronic signature. * * A time mark of the electronic signature provided by a trusted service provider. * * This clause describes a validation process for AdES-T signatures. * * */ public class AdESTValidation { private static final Logger LOG = LoggerFactory.getLogger(AdESTValidation.class); // Primary inputs /** * See {@link eu.europa.esig.dss.validation.policy.ProcessParameters#getDiagnosticData()} * * @return */ private XmlDom diagnosticData; /** * See {@link eu.europa.esig.dss.validation.policy.ProcessParameters#getValidationPolicy()} * * @return */ private ValidationPolicy constraintData; // Secondary inputs /** * See {@link eu.europa.esig.dss.validation.policy.ProcessParameters#getCurrentTime()} * * @return */ private Date currentTime; /** * See {@link eu.europa.esig.dss.validation.policy.ProcessParameters#getBvData()} * * @return */ private XmlDom basicValidationData; private XmlDom timestampValidationData; // Basic Building Blocks for timestamps // local helper variable private XmlNode conclusionNode; private XmlDom bvpConclusion; private String bvpIndication; private String bvpSubIndication; /** * This node is used to add the constraint nodes. */ private XmlNode signatureXmlNode; /** * Represents the {@code XmlDom} of the signature currently being validated */ private XmlDom signatureXmlDom; /** * Represents the identifier of the signature currently being validated */ private String signatureId; /** * Represents the {@code XmlDom} of the timestamp currently being validated */ private XmlNode timestampXmlNode; /** * Represents the identifier of the timestamp currently being validated */ private String timestampId; private Date bestSignatureTime; private void prepareParameters(final XmlNode mainNode, final ProcessParameters params) { this.diagnosticData = params.getDiagnosticData(); this.currentTime = params.getCurrentTime(); isInitialised(mainNode, params); } /** * Checks if each necessary data needed to carry out the validation process is present. The process can be called * from different contexts. This method calls automatically the necessary sub processes to prepare all input data. * * @param params */ private void isInitialised(final XmlNode mainNode, final ProcessParameters params) { if (diagnosticData == null) { throw new DSSException(String.format(ExceptionMessage.EXCEPTION_TCOPPNTBI, getClass().getSimpleName(), "diagnosticData")); } if (params.getValidationPolicy() == null) { throw new DSSException(String.format(ExceptionMessage.EXCEPTION_TCOPPNTBI, getClass().getSimpleName(), "validationPolicy")); } if (currentTime == null) { currentTime = new Date(); params.setCurrentTime(currentTime); } if (basicValidationData == null) { /** * The execution of the Basic Validation process which creates the basic validation data.<br> */ final BasicValidation basicValidation = new BasicValidation(); basicValidationData = basicValidation.run(mainNode, params); } if (timestampValidationData == null) { /** * This executes the Basic Building Blocks process for timestamps present in the signature.<br> * This process needs the diagnostic and policy data. It creates the timestamps validation data. */ final TimestampValidation timeStampValidation = new TimestampValidation(); timestampValidationData = timeStampValidation.run(mainNode, params); } } /** * This method runs the AdES-T validation process. * * 8.2 Inputs<br> * - Signature ..................... Mandatory<br> * - Signed data object (s) ........ Optional<br> * - Trusted-status Service Lists .. Optional<br> * - Signature Validation Policies . Optional<br> * - Local configuration ........... Optional<br> * - Signer's Certificate .......... Optional<br> * * 8.3 Outputs<BR> * The main output of the signature validation is a status indicating the validity of the signature. This status may * be accompanied by additional information (see clause 4). * * 8.4 Processing<BR> * The following steps shall be performed: * * @param params * @return */ public XmlDom run(final XmlNode mainNode, final ProcessParameters params) { prepareParameters(mainNode, params); LOG.debug(this.getClass().getSimpleName() + ": start."); // This script is a validation process for AdES-T signatures. XmlNode adestValidationData = mainNode.addChild(NodeName.ADEST_VALIDATION_DATA); /** * 1) Initialise the set of signature time-stamp tokens from the signature time-stamp properties/attributes * present in the signature and initialise the best-signature-time to the current time. * * NOTE 1: Best-signature-time is an internal variable for the algorithm denoting the earliest time when it can be * proven that a signature has existed. */ final List<XmlDom> signatures = diagnosticData.getElements("/DiagnosticData/Signature"); for (final XmlDom signature : signatures) { signatureXmlDom = signature; signatureId = signature.getValue("./@Id"); final String type = signature.getValue("./@Type"); if (AttributeValue.COUNTERSIGNATURE.equals(type)) { params.setCurrentValidationPolicy(params.getCountersignatureValidationPolicy()); } else { params.setCurrentValidationPolicy(params.getValidationPolicy()); } constraintData = params.getCurrentValidationPolicy(); signatureXmlNode = adestValidationData.addChild(NodeName.SIGNATURE); signatureXmlNode.setAttribute(AttributeName.ID, signatureId); // current time bestSignatureTime = currentTime; final Conclusion conclusion = process(); final XmlNode conclusionXmlNode = conclusion.toXmlNode(); signatureXmlNode.addChild(conclusionXmlNode); } final XmlDom atvDom = adestValidationData.toXmlDom(); params.setAdestData(atvDom); return atvDom; } /** * 2) Signature validation: Perform the validation process for BES signatures (see clause 6) with all the inputs, including the processing of any signed attributes/properties * as specified. If this validation outputs VALID, INDETERMINATE/CRYPTO_CONSTRAINTS_FAILURE_NO_POE, INDETERMINATE/REVOKED_NO_POE or INDETERMINATE/OUT_OF_BOUNDS_NO_POE, go to * the next step. Otherwise, terminate with the returned status and information. * * * @return the {@code Conclusion} which indicates the result of the process */ private Conclusion process() { final Conclusion signatureConclusion = new Conclusion(); bvpConclusion = basicValidationData .getElement("/" + NodeName.BASIC_VALIDATION_DATA + "/Signature[@Id='%s']/Conclusion", signatureId); bvpIndication = bvpConclusion.getValue("./Indication/text()"); bvpSubIndication = bvpConclusion.getValue("./SubIndication/text()"); if (!checkBasicValidationProcessConclusionConstraint(signatureConclusion)) { return signatureConclusion; } /** * 3) Verification of time-marks: the verification of time-marks is out of the scope of the present document. If * the SVA accepts a time-mark as trustworthy (based on out-of-band mechanisms) and if the indicated time is * before the best-signature-time, set best-signature-time to the indicated time. * * --> The DSS framework does not handle the time-marks. */ // This is the list of acceptable timestamps final List<String> rightTimestamps = new ArrayList<String>(); final List<XmlDom> timestamps = signatureXmlDom.getElements("./Timestamps/Timestamp[@Type='%s']", TimestampType.SIGNATURE_TIMESTAMP); timestamps.addAll( signatureXmlDom.getElements("./Timestamps/Timestamp[@Type='%s']", TimestampType.CONTENT_TIMESTAMP)); timestamps.addAll(signatureXmlDom.getElements("./Timestamps/Timestamp[@Type='%s']", TimestampType.ALL_DATA_OBJECTS_TIMESTAMP)); timestamps.addAll(signatureXmlDom.getElements("./Timestamps/Timestamp[@Type='%s']", TimestampType.INDIVIDUAL_DATA_OBJECTS_TIMESTAMP)); boolean found = false; int noContentTimestampCount = 0; for (final XmlDom timestamp : timestamps) { // timestampX timestampId = timestamp.getValue("./@Id"); final String timestampTypeString = timestamp.getValue("./@Type"); final TimestampType timestampType = TimestampType.valueOf(timestampTypeString); final boolean contentTimestamp = isContentTimestamp(timestampType); final Date productionTime = timestamp.getTimeValue("./ProductionTime/text()"); if (!contentTimestamp) { noContentTimestampCount++; } timestampXmlNode = signatureXmlNode.addChild(NodeName.TIMESTAMP); timestampXmlNode.setAttribute(AttributeName.ID, timestampId); timestampXmlNode.setAttribute(AttributeName.TYPE, timestampTypeString); timestampXmlNode.setAttribute(AttributeName.GENERATION_TIME, DSSUtils.formatDate(productionTime)); final Conclusion timestampConclusion = new Conclusion(); if (!checkMessageImprintDataFoundConstraint(timestampConclusion, timestamp)) { continue; } if (!checkMessageImprintDataIntactConstraint(timestampConclusion, timestamp)) { continue; } if (contentTimestamp) { checkTimestampValidationProcessConstraint(); } else { found = checkTimestampValidationProcessConstraint(rightTimestamps, found, productionTime); } } // -1 means that there is no timestamps within the signature // 0 means that there is no valid timestamps // 1 means that there is at least one valid timestamp final int validTimestampCount = noContentTimestampCount == 0 ? -1 : (found ? 1 : 0); if (!checkTimestampsValidationProcessConstraint(signatureConclusion, validTimestampCount)) { return signatureConclusion; } /** * 5) Comparing times: */ /** * NOTE 2: */ if (Indication.INDETERMINATE.equals(bvpIndication) && SubIndication.REVOKED_NO_POE.equals(bvpSubIndication)) { if (!checkRevocationTimeConstraint(signatureConclusion)) { return signatureConclusion; } } /** * NOTE 3: */ if (Indication.INDETERMINATE.equals(bvpIndication) && SubIndication.OUT_OF_BOUNDS_NO_POE.equals(bvpSubIndication)) { if (!checkBestSignatureTimeBeforeIssuanceDateOfSigningCertificateConstraint(signatureConclusion)) { return signatureConclusion; } if (!checkSigningCertificateValidityAtBestSignatureTimeConstraint(signatureConclusion)) { return signatureConclusion; } } /** * NOTE 4: */ if (Indication.INDETERMINATE.equals(bvpIndication) && SubIndication.CRYPTO_CONSTRAINTS_FAILURE_NO_POE.equals(bvpSubIndication)) { if (!checkAlgorithmReliableAtBestSignatureTimeConstraint(signatureConclusion)) { return signatureConclusion; } } if (!checkTimestampCoherenceConstraint(signatureConclusion)) { return signatureConclusion; } if (!checkSigningTimeProperty(signatureConclusion)) { return signatureConclusion; } if (!checkTimestampDelay(signatureConclusion)) { return signatureConclusion; } // This validation process returns VALID signatureConclusion.setIndication(Indication.VALID); final String formatedBestSignatureTime = DSSUtils.formatDate(bestSignatureTime); signatureConclusion.addInfo(MessageTag.EMPTY).setAttribute(AttributeValue.BEST_SIGNATURE_TIME, formatedBestSignatureTime); return signatureConclusion; } private boolean isContentTimestamp(TimestampType timestampType) { return (TimestampType.ALL_DATA_OBJECTS_TIMESTAMP == timestampType) || (TimestampType.INDIVIDUAL_DATA_OBJECTS_TIMESTAMP == timestampType) || (TimestampType.CONTENT_TIMESTAMP == timestampType); } private Date getLatestTimestampProductionDate(final List<XmlDom> timestamps, final TimestampType selectedTimestampType) { Date latestProductionTime = null; for (final XmlDom timestamp : timestamps) { final String timestampType = timestamp.getValue("./@Type"); if (!selectedTimestampType.name().equals(timestampType)) { continue; } final Date productionTime = timestamp.getTimeValue("./ProductionTime/text()"); if ((latestProductionTime == null) || latestProductionTime.before(productionTime)) { latestProductionTime = productionTime; } } return latestProductionTime; } /** * b) Time-stamp token validation: For each time-stamp token remaining in the set of signature time-stamp * tokens, the SVA shall perform the time-stamp validation process (see clause 7):<br/> * * If VALID is returned and if the returned generation time is before best-signature-time, set * best-signature-time to this date and try the next token.<br/> * * In all remaining cases, remove the time-stamp token from the set of signature time-stamp tokens and try * the next token.<br/> * * @param rightTimestamps the {@code List} containing the id of valid valid timestamps * @param found indicates if there is at least one valid timestamp * @param productionTime the production {@code Date} of the current timestamp * @return */ private boolean checkTimestampValidationProcessConstraint(final List<String> rightTimestamps, final boolean found, final Date productionTime) { final XmlDom tspvData = timestampValidationData.getElement( "/TimestampValidationData/Signature[@Id='%s']/Timestamp[@Id='%s']", signatureId, timestampId); final XmlDom tsvpConclusion = tspvData.getElement("./BasicBuildingBlocks/Conclusion"); final String tsvpIndication = tsvpConclusion.getValue("./Indication/text()"); final String tsvpSubIndication = tsvpConclusion.getValue("./SubIndication/text()"); final XmlNode constraintNode = addConstraint(timestampXmlNode, MessageTag.ADEST_ITVPC); boolean valid = Indication.VALID.equals(tsvpIndication); boolean cryptoConstraintsFailureNoPoe = SubIndication.CRYPTO_CONSTRAINTS_FAILURE_NO_POE .equals(tsvpSubIndication); boolean revokedNoPoe = SubIndication.REVOKED_NO_POE.equals(tsvpSubIndication); boolean outOfBoundsNoPoe = SubIndication.OUT_OF_BOUNDS_NO_POE.equals(tsvpSubIndication); if (valid || cryptoConstraintsFailureNoPoe || revokedNoPoe || outOfBoundsNoPoe) { if (productionTime.before(bestSignatureTime)) { constraintNode.addChild(NodeName.STATUS, NodeValue.OK); bestSignatureTime = productionTime; constraintNode.addChild(NodeName.INFO, MessageTag.ADEST_ITVPC_INFO_1); rightTimestamps.add(timestampId); return true; } else { constraintNode.addChild(NodeName.STATUS, NodeValue.KO); constraintNode.addChild(NodeName.WARNING, MessageTag.ADEST_ITVPC_ANS_1); } return found; } constraintNode.addChild(NodeName.STATUS, NodeValue.KO); constraintNode.addChild(NodeName.ERROR, MessageTag.ADEST_ITVPC_ANS_2); // TODO: (Bob: 2014 Mar 15) the information from the timestamp validation process should be copied. return found; } /** * Same as previous method ({@code #checkTimestampValidationProcessConstraint}), but does not add the timestamp to the list of right timestamps, and does not return any result * -> Only performs the functional validation of the timestamp * * @return */ private void checkTimestampValidationProcessConstraint() { final XmlDom tspvData = timestampValidationData.getElement( "/TimestampValidationData/Signature[@Id='%s']/Timestamp[@Id='%s']", signatureId, timestampId); final XmlDom tsvpConclusion = tspvData.getElement("./BasicBuildingBlocks/Conclusion"); final String tsvpIndication = tsvpConclusion.getValue("./Indication/text()"); final XmlNode constraintNode = addConstraint(timestampXmlNode, MessageTag.ADEST_ITVPC); if (Indication.VALID.equals(tsvpIndication)) { constraintNode.addChild(NodeName.STATUS, NodeValue.OK); constraintNode.addChild(NodeName.INFO, MessageTag.ADEST_ITVPC_INFO_1); } else { constraintNode.addChild(NodeName.STATUS, NodeValue.KO); constraintNode.addChild(NodeName.ERROR, MessageTag.ADEST_ITVPC_ANS_2); } } private Date getEarliestTimestampProductionTime(final List<XmlDom> timestamps, final TimestampType selectedTimestampType) { Date earliestProductionTime = null; for (final XmlDom timestamp : timestamps) { final String timestampType = timestamp.getValue("./@Type"); if (!selectedTimestampType.name().equals(timestampType)) { continue; } final Date productionTime = timestamp.getTimeValue("./ProductionTime/text()"); if ((earliestProductionTime == null) || earliestProductionTime.after(productionTime)) { earliestProductionTime = productionTime; } } return earliestProductionTime; } /** * @param parent * @param messageTag * @return */ private XmlNode addConstraint(final XmlNode parent, final MessageTag messageTag) { XmlNode constraintNode = parent.addChild(NodeName.CONSTRAINT); constraintNode.addChild(NodeName.NAME, messageTag.getMessage()).setAttribute(AttributeName.NAME_ID, messageTag.name()); return constraintNode; } /** * Check of: the result of the basic validation process * * NOTE 2: We continue the process in the case INDETERMINATE/REVOKED_NO_POE, because a proof that the signing * occurred before the revocation date may help to go from INDETERMINATE to VALID (step 5-a). * * NOTE 3: We continue the process in the case INDETERMINATE/OUT_OF_BOUNDS_NO_POE, because a proof that the * signing occurred before the issuance date (notBefore) of the signer's certificate may help to go from * INDETERMINATE to INVALID (step 5-b). * * NOTE 4: We continue the process in the case INDETERMINATE/CRYPTO_CONSTRAINTS_FAILURE_NO_POE, because a proof * that the signing occurred before the time one of the algorithms used was no longer considered secure may help * to go from INDETERMINATE to VALID (step 5-c). * * AT: Problem of the revocation of the certificate after signing time --> Following the Austrian's laws the signature is still valid because the timestamps are not * mandatory, what is an aberration. To obtain the validity of such a signature the rule which checks the revocation data should be set as WARN. Then here VALID * indication is obtained. * * @param conclusion the conclusion to use to add the result of the check. * @return false if the check failed and the process should stop, true otherwise. */ private boolean checkBasicValidationProcessConclusionConstraint(final Conclusion conclusion) { final BasicValidationProcessValidConstraint constraint = constraintData .getBasicValidationProcessConclusionConstraint(); if (constraint == null) { return true; } constraint.create(signatureXmlNode, MessageTag.ADEST_ROBVPIIC); boolean validBasicValidationProcess = Indication.VALID.equals(bvpIndication) || (Indication.INDETERMINATE.equals(bvpIndication) && (RuleUtils.in(bvpSubIndication, SubIndication.CRYPTO_CONSTRAINTS_FAILURE_NO_POE, SubIndication.OUT_OF_BOUNDS_NO_POE, SubIndication.REVOKED_NO_POE))); constraint.setValue(validBasicValidationProcess); constraint.setBasicValidationProcessConclusionNode(bvpConclusion); constraint.setConclusionReceiver(conclusion); return constraint.check(); } /** * Check of: is the timestamp message imprint data found * * 4) Signature time-stamp validation: Perform the following steps: * * a) Message imprint verification: For each time-stamp token in the set of signature time-stamp tokens, do the * message imprint verification as specified in clauses 8.4.1 or 8.4.2 depending on the type of the signature. * If the verification fails, remove the token from the set. * * @param conclusion the conclusion to use to add the result of the check. * @param timestamp * @return false if the check failed and the process should stop, true otherwise. */ private boolean checkMessageImprintDataFoundConstraint(final Conclusion conclusion, final XmlDom timestamp) { final Constraint constraint = constraintData.getMessageImprintDataFoundConstraint(); if (constraint == null) { return true; } constraint.create(timestampXmlNode, MessageTag.ADEST_IMIDF); final boolean messageImprintDataIntact = timestamp .getBoolValue(ValidationXPathQueryHolder.XP_MESSAGE_IMPRINT_DATA_FOUND); constraint.setValue(messageImprintDataIntact); constraint.setIndications(MessageTag.ADEST_IMIDF_ANS); constraint.setConclusionReceiver(conclusion); return constraint.check(); } /** * Check of: is the timestamp message imprint data intact * * @param conclusion the conclusion to use to add the result of the check. * @param timestamp * @return false if the check failed and the process should stop, true otherwise. */ private boolean checkMessageImprintDataIntactConstraint(final Conclusion conclusion, final XmlDom timestamp) { final Constraint constraint = constraintData.getMessageImprintDataIntactConstraint(); if (constraint == null) { return true; } constraint.create(timestampXmlNode, MessageTag.ADEST_IMIVC); final boolean messageImprintDataIntact = timestamp .getBoolValue(ValidationXPathQueryHolder.XP_MESSAGE_IMPRINT_DATA_INTACT); constraint.setValue(messageImprintDataIntact); constraint.setIndications(MessageTag.ADEST_IMIVC_ANS); constraint.setConclusionReceiver(conclusion); return constraint.check(); } /** * Check of: Is the result of the timestamps validation process conclusive? * * @param conclusion the conclusion to use to add the result of the check. * @param validTimestampCount * @return false if the check failed and the process should stop, true otherwise. */ private boolean checkTimestampsValidationProcessConstraint(final Conclusion conclusion, final int validTimestampCount) { final TimestampValidationProcessValidConstraint constraint = constraintData .getTimestampValidationProcessConstraint(); if (constraint == null) { return true; } constraint.create(signatureXmlNode, MessageTag.ADEST_ROTVPIIC); constraint.setValidTimestampCount(validTimestampCount); constraint.setIndications(Indication.INDETERMINATE, null, MessageTag.ADEST_ROTVPIIC_ANS); constraint.setSubIndication1(SubIndication.NO_VALID_TIMESTAMP); constraint.setSubIndication2(SubIndication.NO_TIMESTAMP); final String formattedBestSignatureTime = DSSUtils.formatDate(bestSignatureTime); constraint.setAttribute(AttributeValue.BEST_SIGNATURE_TIME, formattedBestSignatureTime); constraint.setConclusionReceiver(conclusion); return constraint.check(); } /** * Check of: Is revocation time posterior to best-signature-time? * * a) If step 2 returned INDETERMINATE/REVOKED_NO_POE: If the returned revocation time is posterior to * best-signature-time, perform step 5d. Otherwise, terminate with INDETERMINATE/REVOKED_NO_POE. In addition to * the data items returned in steps 1 and 2, the SVA should notify the DA with the reason of the failure. * * @param conclusion the conclusion to use to add the result of the check. * @return false if the check failed and the process should stop, true otherwise. */ private boolean checkRevocationTimeConstraint(final Conclusion conclusion) { final Constraint constraint = constraintData.getRevocationTimeConstraint(); if (constraint == null) { return true; } constraint.create(signatureXmlNode, MessageTag.ADEST_IRTPTBST); constraint.setIndications(Indication.INDETERMINATE, SubIndication.REVOKED_NO_POE, MessageTag.ADEST_IRTPTBST_ANS); final Date revocationDate = bvpConclusion.getTimeValue("./Error/@RevocationTime"); final boolean before = bestSignatureTime.before(revocationDate); constraint.setValue(before); final String formatedBestSignatureTime = DSSUtils.formatDate(bestSignatureTime); constraint.setAttribute(AttributeValue.BEST_SIGNATURE_TIME, formatedBestSignatureTime); // TODO: (Bob: 2014 Mar 16) // final XmlDom errorXmlDom = bvpConclusion.getElement("./Error"); // conclusionNode.addChild(errorXmlDom); constraint.setConclusionReceiver(conclusion); return constraint.check(); } /** * Check of: best-signature-time against the signing certificate issuance date. * * b) If step 2 returned INDETERMINATE/OUT_OF_BOUNDS_NO_POE: If best-signature-time is before the issuance date * of the signer's certificate, terminate with INVALID/NOT_YET_VALID. * * NOTE 5: In the algorithm above, the signature-time-stamp protects the signature against the revocation of * the signer's certificate (step 5-a) but not against expiration. The latter case requires validating the * signer's certificate in the past (see clause 9). * * @param conclusion the conclusion to use to add the result of the check. * @return false if the check failed and the process should stop, true otherwise. */ private boolean checkBestSignatureTimeBeforeIssuanceDateOfSigningCertificateConstraint( final Conclusion conclusion) { final Constraint constraint = constraintData .getBestSignatureTimeBeforeIssuanceDateOfSigningCertificateConstraint(); if (constraint == null) { return true; } constraint.create(signatureXmlNode, MessageTag.TSV_IBSTAIDOSC); constraint.setIndications(Indication.INVALID, SubIndication.NOT_YET_VALID, MessageTag.TSV_IBSTAIDOSC_ANS); final String formatedNotBefore = bvpConclusion.getValue("./Error/@NotBefore"); final Date notBeforeTime = DSSUtils.parseDate(formatedNotBefore); final boolean notBefore = !bestSignatureTime.before(notBeforeTime); constraint.setValue(notBefore); final String formatedBestSignatureTime = DSSUtils.formatDate(bestSignatureTime); constraint.setAttribute(AttributeValue.BEST_SIGNATURE_TIME, formatedBestSignatureTime); constraint.setAttribute(AttributeValue.NOT_BEFORE, formatedNotBefore); // TODO: (Bob: 2014 Mar 16) // final XmlDom errorXmlDom = bvpConclusion.getElement("./Error"); // conclusionNode.addChild(errorXmlDom); constraint.setConclusionReceiver(conclusion); return constraint.check(); } /** * Check of: best-signature-time against the signing certificate issuance date. * * b) If step 2 returned INDETERMINATE/OUT_OF_BOUNDS_NO_POE: If best-signature-time is not before the issuance date * of the signer's certificate terminate with INDETERMINATE/OUT_OF_BOUNDS_NO_POE. In addition to the data items returned * in steps 1 and 2, the SVA should notify the DA with the reason of the failure. * * NOTE 5: In the algorithm above, the signature-time-stamp protects the signature against the revocation of * the signer's certificate (step 5-a) but not against expiration. The latter case requires validating the * signer's certificate in the past (see clause 9). * * @param conclusion the conclusion to use to add the result of the check. * @return false if the check failed and the process should stop, true otherwise. */ private boolean checkSigningCertificateValidityAtBestSignatureTimeConstraint(final Conclusion conclusion) { final Constraint constraint = constraintData.getSigningCertificateValidityAtBestSignatureTimeConstraint(); if (constraint == null) { return true; } constraint.create(signatureXmlNode, MessageTag.TSV_ISCNVABST); constraint.setIndications(Indication.INDETERMINATE, SubIndication.OUT_OF_BOUNDS_NO_POE, MessageTag.TSV_ISCNVABST_ANS); // false is always returned: this corresponds to: Otherwise, terminate with INDETERMINATE/OUT_OF_BOUNDS_NO_POE. constraint.setValue(false); final String formatedBestSignatureTime = DSSUtils.formatDate(bestSignatureTime); constraint.setAttribute(AttributeValue.BEST_SIGNATURE_TIME, formatedBestSignatureTime); // TODO: (Bob: 2014 Mar 16) // final XmlDom errorXmlDom = bvpConclusion.getElement("./Error"); // conclusionNode.addChild(errorXmlDom); constraint.setConclusionReceiver(conclusion); return constraint.check(); } /** * Check of: best-signature-time against the signing certificate issuance date. * * c) If step 2 returned INDETERMINATE/CRYPTO_CONSTRAINTS_FAILURE_NO_POE and the material concerned by this * failure is the signature value or a signed attribute, check, if the algorithm(s) concerned were still * considered reliable at best-signature-time, continue with step d. Otherwise, terminate with * INDETERMINATE/CRYPTO_CONSTRAINTS_FAILURE_NO_POE. * * @param conclusion the conclusion to use to add the result of the check. * @return false if the check failed and the process should stop, true otherwise. */ private boolean checkAlgorithmReliableAtBestSignatureTimeConstraint(final Conclusion conclusion) { final Constraint constraint = constraintData.getAlgorithmReliableAtBestSignatureTimeConstraint(); if (constraint == null) { return true; } constraint.create(signatureXmlNode, MessageTag.TSV_WACRABST); constraint.setIndications(Indication.INDETERMINATE, SubIndication.CRYPTO_CONSTRAINTS_FAILURE_NO_POE, MessageTag.TSV_WACRABST_ANS); boolean ok = false; final XmlDom error = bvpConclusion.getElement("./Error"); if (error != null) { final String algorithmExpirationDateString = error.getAttribute("AlgorithmExpirationDate"); if (StringUtils.isNotBlank(algorithmExpirationDateString)) { final Date algorithmExpirationDate = DSSUtils.parseDate(DSSUtils.DEFAULT_DATE_FORMAT, algorithmExpirationDateString); if (!algorithmExpirationDate.before(bestSignatureTime)) { ok = true; } } } constraint.setValue(ok); final String formatedBestSignatureTime = DSSUtils.formatDate(bestSignatureTime); constraint.setAttribute(AttributeValue.BEST_SIGNATURE_TIME, formatedBestSignatureTime); // TODO: (Bob: 2014 Mar 16) // final XmlDom errorXmlDom = bvpConclusion.getElement("./Error"); // conclusionNode.addChild(errorXmlDom); constraint.setConclusionReceiver(conclusion); return constraint.check(); } /** * d) For each time-stamp token remaining in the set of signature time-stamp tokens, check the coherence in the * values of the times indicated in the time-stamp tokens. They shall be posterior to the times indicated in any * time-stamp token computed on the signed data (i.e. any content-time-stamp signed attributes in CAdES or any * AllDataObjectsTimeStamp or IndividualDataObjectsTimeStamp signed present properties in XAdES). The SVA shall * apply the rules specified in RFC 3161 [11], clause 2.4.2 regarding the order of time-stamp tokens generated by * the same or different TSAs given the accuracy and ordering fields' values of the TSTInfo field, unless stated * differently by the Signature Constraints. If all the checks end successfully, go to the next step. Otherwise * return INVALID/TIMESTAMP_ORDER_FAILURE. * * @param conclusion the conclusion to use to add the result of the check. * @return false if the check failed and the process should stop, true otherwise. */ private boolean checkTimestampCoherenceConstraint(final Conclusion conclusion) { final Constraint constraint = constraintData.getTimestampCoherenceConstraint(); if (constraint == null) { return true; } final List<XmlDom> timestamps = signatureXmlDom.getElements("./Timestamps/Timestamp"); for (int index = timestamps.size() - 1; index >= 0; index--) { final XmlDom timestamp = timestamps.get(index); String timestampId = timestamp.getValue("./@Id"); final XmlDom tspvData = timestampValidationData.getElement( "/TimestampValidationData/Signature[@Id='%s']/Timestamp[@Id='%s']", signatureId, timestampId); final XmlDom tsvpConclusion = tspvData.getElement("./BasicBuildingBlocks/Conclusion"); final String tsvpIndication = tsvpConclusion.getValue("./Indication/text()"); if (!Indication.VALID.equals(tsvpIndication)) { timestamps.remove(index); } } Date latestContent = getLatestTimestampProductionDate(timestamps, TimestampType.CONTENT_TIMESTAMP); Date latestContent_ = getLatestTimestampProductionDate(timestamps, TimestampType.ALL_DATA_OBJECTS_TIMESTAMP); latestContent = getLatestDate(latestContent, latestContent_); latestContent_ = getLatestTimestampProductionDate(timestamps, TimestampType.INDIVIDUAL_DATA_OBJECTS_TIMESTAMP); latestContent = getLatestDate(latestContent, latestContent_); final Date earliestSignature = getEarliestTimestampProductionTime(timestamps, TimestampType.SIGNATURE_TIMESTAMP); final Date latestSignature = getLatestTimestampProductionDate(timestamps, TimestampType.SIGNATURE_TIMESTAMP); Date earliestValidationData = getEarliestTimestampProductionTime(timestamps, TimestampType.VALIDATION_DATA_TIMESTAMP); final Date earliestValidationData_ = getEarliestTimestampProductionTime(timestamps, TimestampType.VALIDATION_DATA_REFSONLY_TIMESTAMP); earliestValidationData = getEarliestDate(earliestValidationData, earliestValidationData_); final Date earliestArchive = getEarliestTimestampProductionTime(timestamps, TimestampType.ARCHIVE_TIMESTAMP); Date latestValidationData = getLatestTimestampProductionDate(timestamps, TimestampType.VALIDATION_DATA_TIMESTAMP); final Date latestValidationData_ = getLatestTimestampProductionDate(timestamps, TimestampType.VALIDATION_DATA_REFSONLY_TIMESTAMP); latestValidationData = getLatestDate(latestValidationData, latestValidationData_); if ((latestContent == null) && (earliestSignature == null) && (earliestValidationData == null) && (earliestArchive == null)) { return true; } boolean ok = true; if ((earliestSignature == null) && ((earliestValidationData != null) || (earliestArchive != null))) { ok = false; } // Check content-timestamp against-signature timestamp if ((latestContent != null) && (earliestSignature != null)) { ok = ok && latestContent.before(earliestSignature); } // Check signature-timestamp against validation-data and validation-data-refs-only timestamp if ((latestSignature != null) && (earliestValidationData != null)) { ok = ok && latestSignature.before(earliestValidationData); } // Check archive-timestamp if ((latestSignature != null) && (earliestArchive != null)) { ok = ok && earliestArchive.after(latestSignature); } if ((latestValidationData != null) && (earliestArchive != null)) { ok = ok && earliestArchive.after(latestValidationData); } constraint.create(signatureXmlNode, MessageTag.TSV_ASTPTCT); constraint.setIndications(Indication.INVALID, SubIndication.TIMESTAMP_ORDER_FAILURE, MessageTag.TSV_ASTPTCT_ANS); constraint.setValue(ok); final String formattedLatestContentTimestampProductionDate = DSSUtils.formatDate(latestContent); final String formattedEarliestSignatureTimestampProductionDate = DSSUtils.formatDate(earliestSignature); constraint.setAttribute(AttributeValue.LATEST_CONTENT_TIMESTAMP_PRODUCTION_TIME, formattedLatestContentTimestampProductionDate); constraint.setAttribute(AttributeValue.EARLIEST_SIGNATURE_TIMESTAMP_PRODUCTION_TIME, formattedEarliestSignatureTimestampProductionDate); constraint.setConclusionReceiver(conclusion); return constraint.check(); } private static Date getLatestDate(Date firstDate, final Date secondDate) { if ((firstDate != null) && (secondDate != null)) { if (firstDate.before(secondDate)) { firstDate = secondDate; } } else if (secondDate != null) { firstDate = secondDate; } return firstDate; } private static Date getEarliestDate(Date firstDate, final Date secondDate) { if ((firstDate != null) && (secondDate != null)) { if (firstDate.after(secondDate)) { firstDate = secondDate; } } else if (secondDate != null) { firstDate = secondDate; } return firstDate; } /** * Check of: Time-stamp delay. * * 6) Handling Time-stamp delay: If the validation constraints specify a time-stamp delay, do the following: * * a) If no signing-time property/attribute is present, fail with INDETERMINATE and an explanation that the * validation failed due to the absence of claimed signing time. * * @param conclusion the conclusion to use to add the result of the check. * @return false if the check failed and the process should stop, true otherwise. */ private boolean checkSigningTimeProperty(final Conclusion conclusion) { final Constraint constraint = constraintData.getTimestampDelaySigningTimePropertyConstraint(); if (constraint == null) { return true; } constraint.create(signatureXmlNode, MessageTag.BBB_SAV_ISQPSTP); constraint.setIndications(Indication.INDETERMINATE, SubIndication.CLAIMED_SIGNING_TIME_ABSENT, MessageTag.ADEST_VFDTAOCST_ANS); final String signingTime = signatureXmlDom.getValue("./DateTime/text()"); constraint.setValue(StringUtils.isNotBlank(signingTime)); constraint.setConclusionReceiver(conclusion); return constraint.check(); } /** * Check of: Time-stamp delay. * * b) If a signing-time property/attribute is present, check that the claimed time in the attribute plus the * timestamp delay is after the best-signature-time. If the check is successful, go to the next step. * Otherwise, fail with INVALID/SIG_CONSTRAINTS_FAILURE and an explanation that the validation failed due to * the time-stamp delay constraint. * * @param conclusion the conclusion to use to add the result of the check. * @return false if the check failed and the process should stop, true otherwise. */ private boolean checkTimestampDelay(final Conclusion conclusion) { final Constraint constraint = constraintData.getTimestampDelaySigningTimePropertyConstraint(); if (constraint == null) { return true; } constraint.create(signatureXmlNode, MessageTag.ADEST_ISTPTDABST); constraint.setIndications(Indication.INVALID, SubIndication.SIG_CONSTRAINTS_FAILURE, MessageTag.ADEST_ISTPTDABST_ANS); final Long timestampDelay = constraintData.getTimestampDelayTime(); final String signingTime = signatureXmlDom.getValue("./DateTime/text()"); final Date date = DSSUtils.quietlyParseDate(signingTime); constraint.setValue((date.getTime() + timestampDelay) > bestSignatureTime.getTime()); constraint.setConclusionReceiver(conclusion); return constraint.check(); } }