/*
* DSS - Digital Signature Services
*
* Copyright (C) 2013 European Commission, Directorate-General Internal Market and Services (DG MARKT), B-1049 Bruxelles/Brussel
*
* Developed by: 2013 ARHS Developments S.A. (rue Nicolas Bové 2B, L-1253 Luxembourg) http://www.arhs-developments.com
*
* This file is part of the "DSS - Digital Signature Services" project.
*
* "DSS - Digital Signature Services" is free software: you can redistribute it and/or modify it under the terms of
* the GNU Lesser General Public License as published by the Free Software Foundation, either version 2.1 of the
* License, or (at your option) any later version.
*
* DSS is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along with
* "DSS - Digital Signature Services". If not, see <http://www.gnu.org/licenses/>.
*/
package eu.europa.ec.markt.dss.validation102853.processes;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import eu.europa.ec.markt.dss.exception.DSSException;
import eu.europa.ec.markt.dss.validation102853.TimestampType;
import eu.europa.ec.markt.dss.validation102853.policy.ProcessParameters;
import eu.europa.ec.markt.dss.validation102853.policy.SignatureCryptographicConstraint;
import eu.europa.ec.markt.dss.validation102853.policy.ValidationPolicy;
import eu.europa.ec.markt.dss.validation102853.processes.subprocesses.CryptographicVerification;
import eu.europa.ec.markt.dss.validation102853.processes.subprocesses.IdentificationOfTheSignersCertificate;
import eu.europa.ec.markt.dss.validation102853.processes.subprocesses.X509CertificateValidation;
import eu.europa.ec.markt.dss.validation102853.report.Conclusion;
import eu.europa.ec.markt.dss.validation102853.rules.AttributeName;
import eu.europa.ec.markt.dss.validation102853.rules.AttributeValue;
import eu.europa.ec.markt.dss.validation102853.rules.ExceptionMessage;
import eu.europa.ec.markt.dss.validation102853.rules.Indication;
import eu.europa.ec.markt.dss.validation102853.rules.NodeName;
import eu.europa.ec.markt.dss.validation102853.rules.NodeValue;
import eu.europa.ec.markt.dss.validation102853.rules.SubIndication;
import eu.europa.ec.markt.dss.validation102853.xml.XmlDom;
import eu.europa.ec.markt.dss.validation102853.xml.XmlNode;
import static eu.europa.ec.markt.dss.validation102853.rules.MessageTag.ASCCM;
import static eu.europa.ec.markt.dss.validation102853.rules.MessageTag.EMPTY;
/**
* 7 Validation Process for Time-Stamps<br>
* <br>
* 7.1 Description<br>
* <br>
* This clause describes a process for the validation of an RFC 3161 [11] time-stamp token. An RFC 3161 [11] time-stamp
* token is basically a CAdES-BES signature. Hence, the validation process is built in the validation process of a
* CAdES-BES signature.<br>
*
* @author bielecro
*/
public class TimestampValidation implements Indication, SubIndication, NodeName, NodeValue, AttributeName, AttributeValue, ExceptionMessage, ValidationXPathQueryHolder {
private static final Logger LOG = LoggerFactory.getLogger(TimestampValidation.class);
private XmlDom diagnosticData;
private ValidationPolicy constraintData;
/**
* See {@link ProcessParameters#getCurrentTime()}
*/
private Date currentTime;
private void prepareParameters(final ProcessParameters params) {
this.diagnosticData = params.getDiagnosticData();
this.currentTime = params.getCurrentTime();
isInitialised(params);
}
private void isInitialised(final ProcessParameters params) {
if (diagnosticData == null) {
throw new DSSException(String.format(EXCEPTION_TCOPPNTBI, getClass().getSimpleName(), "diagnosticData"));
}
if (params.getValidationPolicy() == null) {
throw new DSSException(String.format(EXCEPTION_TCOPPNTBI, getClass().getSimpleName(), "validationPolicy"));
}
if (currentTime == null) {
throw new DSSException(String.format(EXCEPTION_TCOPPNTBI, getClass().getSimpleName(), "currentTime"));
}
}
/**
* 7.4 Processing<br>
* <p/>
* The following steps shall be performed:<br>
* <p/>
* 1) Token signature validation: perform the validation process for BES signature (see clause 6) with the time-stamp
* token. In all the steps of this process, take into account that the signature to validate is a timestamp token
* (e.g. to select TSA trust-anchors). If this step ends with a success indication, go to the next step. Otherwise,
* fail with the indication and information returned by the validation process.<br>
* <p/>
* 2) Data extraction: in addition to the data items returned in step 1, the process shall return data items
* extracted from the TSTInfo [11] (the generation time, the message imprint, etc.). These items may be used by the
* SVA in the process of validating the AdES signature.
*
* @param params
* @return
*/
public XmlDom run(final XmlNode mainNode, final ProcessParameters params) {
prepareParameters(params);
LOG.debug(this.getClass().getSimpleName() + ": start.");
final List<XmlDom> signatures = diagnosticData.getElements("/DiagnosticData/Signature");
final XmlNode timestampValidationDataNode = mainNode.addChild(TIMESTAMP_VALIDATION_DATA);
for (final XmlDom signature : signatures) {
final String type = signature.getValue("./@Type");
if (COUNTERSIGNATURE.equals(type)) {
params.setCurrentValidationPolicy(params.getCountersignatureValidationPolicy());
} else {
params.setCurrentValidationPolicy(params.getValidationPolicy());
}
constraintData = params.getCurrentValidationPolicy();
final List<XmlDom> timestamps = new ArrayList<XmlDom>();
final TimestampType[] timestampTypes = TimestampType.values();
for (int ii = 0; ii < timestampTypes.length; ii++) {
final TimestampType timestampType = timestampTypes[ii];
extractTimestamp(signature, timestampType, timestamps);
}
if (timestamps.isEmpty()) {
continue;
}
// This defines the signature context of the execution of the following processes.
params.setSignatureContext(signature);
final String signatureId = signature.getValue("./@Id");
final XmlNode signatureNode = timestampValidationDataNode.addChild(SIGNATURE);
signatureNode.setAttribute(ID, signatureId);
for (final XmlDom timestamp : timestamps) {
final Conclusion conclusion = new Conclusion();
// This defines the context of the execution of the following processes. The same sub-processes are used for
// signature and timestamp validation.
params.setContextName(TIMESTAMP);
params.setContextElement(timestamp);
final String timestampId = timestamp.getValue("./@Id");
final String timestampType = timestamp.getValue("./@Type");
final XmlNode timestampNode = signatureNode.addChild(TIMESTAMP);
timestampNode.setAttribute(ID, timestampId);
timestampNode.setAttribute(TIMESTAMP_TYPE, timestampType);
/**
* 5. Basic Building Blocks
*/
final XmlNode basicBuildingBlocksNode = timestampNode.addChild(BASIC_BUILDING_BLOCKS);
/**
* 5.1. Identification of the signer's certificate (ISC)
*/
final IdentificationOfTheSignersCertificate isc = new IdentificationOfTheSignersCertificate();
final Conclusion iscConclusion = isc.run(params, TIMESTAMP);
basicBuildingBlocksNode.addChild(iscConclusion.getValidationData());
if (!iscConclusion.isValid()) {
basicBuildingBlocksNode.addChild(iscConclusion.toXmlNode());
continue;
}
conclusion.addInfo(iscConclusion);
conclusion.addWarnings(iscConclusion);
/**
* 5.2. Validation Context Initialisation (VCI)
*/
/*
* --> Not needed for Timestamps validation. The constraints are already loaded during the execution of the
* Basic Building Blocks process for the main signature.
*/
/**
* 5.4 Cryptographic Verification (CV)
*/
final CryptographicVerification cv = new CryptographicVerification();
final Conclusion cvConclusion = cv.run(params, basicBuildingBlocksNode);
if (!cvConclusion.isValid()) {
basicBuildingBlocksNode.addChild(cvConclusion.toXmlNode());
continue;
}
conclusion.addInfo(cvConclusion);
conclusion.addWarnings(cvConclusion);
/**
* 5.5 Signature Acceptance Validation (SAV)
*/
final Conclusion savConclusion = runSAV(timestamp, basicBuildingBlocksNode);
if (!savConclusion.isValid()) {
basicBuildingBlocksNode.addChild(savConclusion.toXmlNode());
continue;
}
conclusion.addInfo(savConclusion);
conclusion.addWarnings(savConclusion);
/**
* 5.3 X.509 Certificate Validation (XCV)
*/
final X509CertificateValidation xcv = new X509CertificateValidation();
final Conclusion xcvConclusion = xcv.run(params, TIMESTAMP);
basicBuildingBlocksNode.addChild(xcvConclusion.getValidationData());
if (!xcvConclusion.isValid()) {
basicBuildingBlocksNode.addChild(xcvConclusion.toXmlNode());
continue;
}
conclusion.addInfo(xcvConclusion);
conclusion.addWarnings(xcvConclusion);
conclusion.setIndication(VALID);
final XmlNode conclusionXmlNode = conclusion.toXmlNode();
basicBuildingBlocksNode.addChild(conclusionXmlNode);
}
}
final XmlDom tsDom = timestampValidationDataNode.toXmlDom();
params.setTsData(tsDom);
return tsDom;
}
/**
* This method extracts all timestamps from the {@code XmlDom} signature representation and adds them to the timestamp {@code List}
*
* @param signature {@code XmlDom} representation of the signature
* @param timestampType
* @param timestamps the {@code List} of the all extracted timestamps
*/
private void extractTimestamp(final XmlDom signature, final TimestampType timestampType, final List<XmlDom> timestamps) {
final String xPath = "./Timestamps/Timestamp[@Type='%s']";
final List<XmlDom> extractedTimestamps = signature.getElements(xPath, timestampType);
timestamps.addAll(extractedTimestamps);
}
/**
* The SAV process for a timestamp is far simpler than that of the principal signature. This is why a specific method
* is dedicated to its treatment.
*
* @param timestampXmlDom
* @param processNode the parent process {@code XmlNode} to use to include the validation information
* @return the {@code Conclusion} which indicates the result of the process
*/
private Conclusion runSAV(final XmlDom timestampXmlDom, final XmlNode processNode) {
/**
* 5.5 Signature Acceptance Validation (SAV)
*/
final XmlNode subProcessNode = processNode.addChild(SAV);
final Conclusion conclusion = processSAV(timestampXmlDom, subProcessNode);
final XmlNode conclusionXmlNode = conclusion.toXmlNode();
subProcessNode.addChild(conclusionXmlNode);
return conclusion;
}
/**
* 5.5.4 Processing<br>
* <p/>
* This process consists in checking the Signature and Cryptographic Constraints against the signature. The general
* principle is as follows: perform the following for each constraint:<br>
* <p/>
* • If the constraint necessitates processing a property/attribute in the signature, perform the processing of the
* property/attribute as specified from clause 5.5.4.1 to 5.5.4.8. <b>--> The DSS framework does not handle the
* constraints concerning timestamps.</b><br>
* <p/>
* • If at least one of the algorithms that have been used in validation of the signature or the size of the keys
* used with such an algorithm is no longer considered reliable, return
* INDETERMINATE/CRYPTO_CONSTRAINTS_FAILURE_NO_POE together with the list of algorithms and key sizes, if applicable,
* that are concerned and the time for each of the algorithms up to which the resp. algorithm was considered secure.
*
* @param timestampXmlDom
* @param subProcessNode
* @return the {@code Conclusion} which indicates the result of the process.
*/
private Conclusion processSAV(final XmlDom timestampXmlDom, final XmlNode subProcessNode) {
final Conclusion conclusion = new Conclusion();
final SignatureCryptographicConstraint signatureConstraint = constraintData.getSignatureCryptographicConstraint(TIMESTAMP);
if (signatureConstraint != null) {
signatureConstraint.create(subProcessNode, ASCCM);
signatureConstraint.setCurrentTime(currentTime);
signatureConstraint.setEncryptionAlgorithm(timestampXmlDom.getValue(XP_ENCRYPTION_ALGO_USED_TO_SIGN_THIS_TOKEN));
signatureConstraint.setDigestAlgorithm(timestampXmlDom.getValue(XP_DIGEST_ALGO_USED_TO_SIGN_THIS_TOKEN));
signatureConstraint.setKeyLength(timestampXmlDom.getValue(XP_KEY_LENGTH_USED_TO_SIGN_THIS_TOKEN));
signatureConstraint.setIndications(INDETERMINATE, CRYPTO_CONSTRAINTS_FAILURE_NO_POE, EMPTY);
signatureConstraint.setConclusionReceiver(conclusion);
if (!signatureConstraint.check()) {
return conclusion;
}
}
final SignatureCryptographicConstraint signingCertificateConstraint = constraintData.getSignatureCryptographicConstraint(TIMESTAMP, SIGNING_CERTIFICATE);
if (signingCertificateConstraint != null) {
signingCertificateConstraint.create(subProcessNode, ASCCM);
signingCertificateConstraint.setCurrentTime(currentTime);
signingCertificateConstraint.setEncryptionAlgorithm(timestampXmlDom.getValue(XP_ENCRYPTION_ALGO_USED_TO_SIGN_THIS_TOKEN));
signingCertificateConstraint.setDigestAlgorithm(timestampXmlDom.getValue(XP_DIGEST_ALGO_USED_TO_SIGN_THIS_TOKEN));
signingCertificateConstraint.setKeyLength(timestampXmlDom.getValue(XP_KEY_LENGTH_USED_TO_SIGN_THIS_TOKEN));
signingCertificateConstraint.setIndications(INDETERMINATE, CRYPTO_CONSTRAINTS_FAILURE_NO_POE, EMPTY);
signingCertificateConstraint.setConclusionReceiver(conclusion);
if (!signingCertificateConstraint.check()) {
return conclusion;
}
}
// This validation process returns VALID
conclusion.setIndication(VALID);
return conclusion;
}
}