/*
* 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.validation102853.processes.subprocesses;
import java.util.Date;
import java.util.List;
import eu.europa.ec.markt.dss.DSSUtils;
import eu.europa.ec.markt.dss.TSLConstant;
import eu.europa.ec.markt.dss.exception.DSSException;
import eu.europa.ec.markt.dss.validation102853.certificate.CertificateSourceType;
import eu.europa.ec.markt.dss.validation102853.policy.CertificateExpirationConstraint;
import eu.europa.ec.markt.dss.validation102853.policy.Constraint;
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.ValidationXPathQueryHolder;
import eu.europa.ec.markt.dss.validation102853.processes.dss.ForLegalPerson;
import eu.europa.ec.markt.dss.validation102853.processes.dss.QualifiedCertificate;
import eu.europa.ec.markt.dss.validation102853.processes.dss.SSCD;
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.RuleConstant;
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.BBB_XCV_ACCM;
import static eu.europa.ec.markt.dss.validation102853.rules.MessageTag.BBB_XCV_CCCBB;
import static eu.europa.ec.markt.dss.validation102853.rules.MessageTag.BBB_XCV_CCCBB_ANS;
import static eu.europa.ec.markt.dss.validation102853.rules.MessageTag.BBB_XCV_CMDCIITLP;
import static eu.europa.ec.markt.dss.validation102853.rules.MessageTag.BBB_XCV_CMDCIITLP_ANS;
import static eu.europa.ec.markt.dss.validation102853.rules.MessageTag.BBB_XCV_CMDCIQC;
import static eu.europa.ec.markt.dss.validation102853.rules.MessageTag.BBB_XCV_CMDCIQC_ANS;
import static eu.europa.ec.markt.dss.validation102853.rules.MessageTag.BBB_XCV_CMDCISSCD;
import static eu.europa.ec.markt.dss.validation102853.rules.MessageTag.BBB_XCV_CMDCISSCD_ANS;
import static eu.europa.ec.markt.dss.validation102853.rules.MessageTag.BBB_XCV_ICSI;
import static eu.europa.ec.markt.dss.validation102853.rules.MessageTag.BBB_XCV_ICSI_ANS;
import static eu.europa.ec.markt.dss.validation102853.rules.MessageTag.BBB_XCV_ICTIVRSC;
import static eu.europa.ec.markt.dss.validation102853.rules.MessageTag.BBB_XCV_ICTIVRSC_ANS;
import static eu.europa.ec.markt.dss.validation102853.rules.MessageTag.BBB_XCV_IICR;
import static eu.europa.ec.markt.dss.validation102853.rules.MessageTag.BBB_XCV_IICR_ANS;
import static eu.europa.ec.markt.dss.validation102853.rules.MessageTag.BBB_XCV_IRDPFC;
import static eu.europa.ec.markt.dss.validation102853.rules.MessageTag.BBB_XCV_IRDPFC_ANS;
import static eu.europa.ec.markt.dss.validation102853.rules.MessageTag.BBB_XCV_IRDTFC;
import static eu.europa.ec.markt.dss.validation102853.rules.MessageTag.BBB_XCV_IRDTFC_ANS;
import static eu.europa.ec.markt.dss.validation102853.rules.MessageTag.BBB_XCV_IRIF;
import static eu.europa.ec.markt.dss.validation102853.rules.MessageTag.BBB_XCV_IRIF_ANS;
import static eu.europa.ec.markt.dss.validation102853.rules.MessageTag.BBB_XCV_ISCGKU;
import static eu.europa.ec.markt.dss.validation102853.rules.MessageTag.BBB_XCV_ISCGKU_ANS;
import static eu.europa.ec.markt.dss.validation102853.rules.MessageTag.BBB_XCV_ISCOH;
import static eu.europa.ec.markt.dss.validation102853.rules.MessageTag.BBB_XCV_ISCOH_ANS;
import static eu.europa.ec.markt.dss.validation102853.rules.MessageTag.BBB_XCV_ISCR;
import static eu.europa.ec.markt.dss.validation102853.rules.MessageTag.BBB_XCV_ISCR_ANS;
import static eu.europa.ec.markt.dss.validation102853.rules.MessageTag.CTS_IIDOCWVPOTS;
import static eu.europa.ec.markt.dss.validation102853.rules.MessageTag.CTS_IIDOCWVPOTS_ANS;
import static eu.europa.ec.markt.dss.validation102853.rules.MessageTag.CTS_ITACBT;
import static eu.europa.ec.markt.dss.validation102853.rules.MessageTag.CTS_ITACBT_ANS;
import static eu.europa.ec.markt.dss.validation102853.rules.MessageTag.CTS_WITSS;
import static eu.europa.ec.markt.dss.validation102853.rules.MessageTag.CTS_WITSS_ANS;
import static eu.europa.ec.markt.dss.validation102853.rules.MessageTag.EMPTY;
public class X509CertificateValidation implements Indication, SubIndication, NodeName, NodeValue, AttributeName, AttributeValue, ExceptionMessage, RuleConstant, ValidationXPathQueryHolder {
/**
* The following variables are used only in order to simplify the writing of the rules!
*/
/**
* See {@link ProcessParameters#getDiagnosticData()}
*/
private XmlDom diagnosticData;
/**
* See {@link ProcessParameters#getCurrentValidationPolicy()}
*/
protected ValidationPolicy constraintData;
/**
* See {@link ProcessParameters#getCurrentTime()}
*/
private Date currentTime;
/**
* See {@link ProcessParameters#getSignatureContext()}
*/
private XmlDom signatureContext;
/**
* See {@link ProcessParameters#getContextElement()}
*/
protected XmlDom contextElement;
/**
* // TODO: (Bob: 2014 Mar 12)
*/
private String contextName;
/**
* See {@link ProcessParameters#getSigningCertificateId()}
*/
private String signingCertificateId;
/**
* See {@link ProcessParameters#getSigningCertificate()}
*/
private XmlDom signingCertificate;
/**
* This node is used to add the constraint nodes.
*/
protected XmlNode validationDataXmlNode;
private void prepareParameters(final ProcessParameters params) {
this.diagnosticData = params.getDiagnosticData();
this.constraintData = params.getCurrentValidationPolicy();
this.signatureContext = params.getSignatureContext();
this.contextElement = params.getContextElement();
this.currentTime = params.getCurrentTime();
this.signingCertificateId = params.getSigningCertificateId();
this.signingCertificate = params.getSigningCertificate();
isInitialised(params);
}
private void isInitialised(final ProcessParameters params) {
if (diagnosticData == null) {
throw new DSSException(String.format(EXCEPTION_TCOPPNTBI, getClass().getSimpleName(), "diagnosticData"));
}
if (constraintData == null) {
throw new DSSException(String.format(EXCEPTION_TCOPPNTBI, getClass().getSimpleName(), "validationPolicy"));
}
if (currentTime == null) {
throw new DSSException(String.format(EXCEPTION_TCOPPNTBI, getClass().getSimpleName(), "currentTime"));
}
if (signatureContext == null) {
throw new DSSException(String.format(EXCEPTION_TCOPPNTBI, getClass().getSimpleName(), "signatureContext"));
}
if (contextElement == null) {
throw new DSSException(String.format(EXCEPTION_TCOPPNTBI, getClass().getSimpleName(), "contextElement"));
}
/*
--> With the Warning system everything is possible
if (signingCertificateId == null) {
throw new DSSException(String.format(EXCEPTION_TCOPPNTBI, getClass().getSimpleName(), "signCertId"));
}
if (signingCertificate == null) {
throw new DSSException(String.format(EXCEPTION_TCOPPNTBI, getClass().getSimpleName(), "signCert"));
}
*/
}
/**
* 5.3 X.509 Certificate Validation (XCV)<br>
* This method carry out the XCV process.
*
* @param params validation process parameters
* @return false if the validation failed, true otherwise
*/
public Conclusion run(final ProcessParameters params, final String contextName) {
this.contextName = contextName;
prepareParameters(params);
validationDataXmlNode = new XmlNode(XCV);
validationDataXmlNode.setNameSpace(XmlDom.NAMESPACE);
final Conclusion conclusion = process(params);
conclusion.setValidationData(validationDataXmlNode);
return conclusion;
}
/**
* 5.3.4 Processing This process consists in the following steps:
*
* @param params validation process parameters
* @return
*/
private Conclusion process(final ProcessParameters params) {
final Conclusion conclusion = new Conclusion();
/**
* 1) Check that the current time is in the validity range of the signer's certificate. If this constraint is not
* satisfied, abort the processing with the indication INDETERMINATE and the sub indication OUT_OF_BOUNDS_NO_POE.
*/
if (!checkCertificateExpirationConstraint(conclusion, contextName, SIGNING_CERTIFICATE)) {
return conclusion;
}
/**
* 2) Build a new prospective certificate chain that has not yet been evaluated. The chain shall satisfy the
* conditions of a prospective certificate chain as stated in [4], clause 6.1, using one of the trust anchors
* provided in the inputs:
*/
final boolean trustedProspectiveCertificateChain = isTrustedProspectiveCertificateChain(params);
if (!checkProspectiveCertificateChainConstraint(conclusion, trustedProspectiveCertificateChain)) {
return conclusion;
}
/**
* b) Otherwise, add this chain to the set of prospected chains and go to step 3.
*/
/**
* 3) Run the Certification Path Validation [4], clause 6.1, with the following inputs:<br>
* - the prospective chain built in the previous step,<br>
* - the trust anchor used in the previous step,<br>
* - the X.509 parameters provided in the inputs and<br>
* - the current date/time.<br>
* The validation shall include revocation checking for each certificate in the chain:
*/
final List<XmlDom> certificateChainXmlDom = contextElement.getElements("./CertificateChain/ChainCertificate");
for (final XmlDom chainCertificateXmlDom : certificateChainXmlDom) {
final String certificateId = chainCertificateXmlDom.getValue("./@Id");
final XmlDom certificateXmlDom = params.getCertificate(certificateId);
final boolean isTrusted = certificateXmlDom.getBoolValue("./Trusted/text()");
if (isTrusted) {
continue;
}
final String subContext;
// The case of other certificates then the signing certificate:
if (!signingCertificateId.equals(certificateId)) {
subContext = CA_CERTIFICATE;
// The check is already done for the signing certificate.
// TODO: (Bob: 2014 Mar 09) Notify ETSI: This step is not indicated in the standard!!!
if (!checkCertificateExpirationConstraint(conclusion, contextName, subContext)) {
return conclusion;
}
} else {
subContext = SIGNING_CERTIFICATE;
if (!checkKeyUsageConstraint(conclusion, certificateId, certificateXmlDom)) {
return conclusion;
}
}
if (!checkCertificateSignatureConstraint(conclusion, certificateId, certificateXmlDom, subContext)) {
return conclusion;
}
if (!checkRevocationDataAvailableConstraint(conclusion, certificateId, certificateXmlDom, subContext)) {
return conclusion;
}
if (!checkRevocationDataIsTrustedConstraint(conclusion, certificateId, certificateXmlDom, subContext)) {
return conclusion;
}
// final String revocationSource = certificateXmlDom.getValue("./Revocation/Source/text()");
// final String revocationSigningCertificateId = certificateXmlDom.getValue("./Revocation/SigningCertificate/@Id");
// final String anchorId = certificateXmlDom.getValue("./Revocation/CertificateChain/ChainCertificate[last()]/@Id");
// if ("OCSPToken".equals(revocationSource)) {
//
// } else if ("CRLToken".equals(revocationSource)) {
//
// }
final XmlDom revocation = certificateXmlDom.getElement("./Revocation");
final String revocationIssuingTimeString = getValue(revocation, "./IssuingTime/text()");
boolean revocationFresh = prepareRevocationFreshnessCheck(revocationIssuingTimeString);
/**
* a) If the certificate path validation returns a success indication and the revocation information used is
* considered fresh, go to the next step.
*/
/*
* --> This is done when other conditions are not met
*/
/**
* b) If the certificate path validation returns a success indication and the revocation information used is
* not considered fresh, abort the process with the indication INDETERMINATE, the sub indication TRY_LATER and
* the content of the NEXT_UPDATE-field of the CRL used as the suggestion for when to try the validation again.
*/
final boolean revocationStatus = getBoolValue(revocation, "./Status/text()");
final String revocationNextUpdate = getValue(revocation, "./NextUpdate/text()");
if (!checkRevocationFreshnessConstraint(conclusion, certificateId, revocationFresh, revocationNextUpdate, revocationIssuingTimeString, subContext)) {
return conclusion;
}
final String revocationReason = getValue(revocation, "./Reason/text()");
final String revocationDatetime = getValue(revocation, "./DateTime/text()");
// The case of the signing certificate:
if (signingCertificateId.equals(certificateId)) {
if (!checkSigningCertificateRevokedConstraint(conclusion, certificateId, revocationStatus, revocationReason, revocationDatetime, subContext)) {
return conclusion;
}
if (!checkSigningCertificateOnHoldConstraint(conclusion, certificateId, revocationStatus, revocationReason, revocationDatetime, revocationNextUpdate, subContext)) {
return conclusion;
}
if (!checkSigningCertificateTSLValidityConstraint(conclusion, certificateId, certificateXmlDom)) {
return conclusion;
}
if (!checkSigningCertificateTSLStatusConstraint(conclusion, certificateId, certificateXmlDom)) {
return conclusion;
}
if (!checkSigningCertificateTSLStatusAndValidityConstraint(conclusion, certificateId, certificateXmlDom)) {
return conclusion;
}
// There is not need to check the revocation data for trusted and self-signed certificates
} else {
// For all certificates different from the signing certificate and trust anchor.
if (!checkIntermediateCertificateRevokedConstraint(conclusion, certificateId, revocationStatus, revocationReason, revocationDatetime, subContext)) {
return conclusion;
}
}
// revocation data signature cryptographic constraints validation
if (!checkCertificateCryptographicConstraint(conclusion, revocation, REVOCATION, subContext)) {
return conclusion;
}
/**
* f) If the certificate path validation returns a failure indication with any other reason, go to step 2.
*/
// --> DSS builds only one chain
} // loop end
if (!checkChainConstraint(conclusion)) {
return conclusion;
}
/**
* A.2 Constraints on X.509 Certificate meta-data
*
* The following constraints are to be applied to the signer's certificate before considering it as valid for the
* intended use.
* --> These constraints apply only to the main signature
*/
if (MAIN_SIGNATURE.equals(contextName)) {
final QualifiedCertificate qc = new QualifiedCertificate(constraintData);
final boolean isQC = qc.run(signingCertificate);
if (!checkSigningCertificateQualificationConstraint(conclusion, isQC)) {
return conclusion;
}
final SSCD sscd = new SSCD(constraintData);
final Boolean isSSCD = sscd.run(signingCertificate);
if (!checkSigningCertificateSupportedBySSCDConstraint(conclusion, isSSCD)) {
return conclusion;
}
final ForLegalPerson forLegalPerson = new ForLegalPerson(constraintData);
final Boolean isForLegalPerson = forLegalPerson.run(signingCertificate);
if (!checkSigningCertificateIssuedToLegalPersonConstraint(conclusion, isForLegalPerson)) {
return conclusion;
}
}
/**
* 5) Apply the cryptographic constraints to the chain. If the chain does not match these constraints, set the
* current status to INDETERMINATE/CRYPTO_CONSTRAINTS_FAILURE_NO_POE and go to step 2.
*/
final String lastChainCertId = contextElement.getValue("./CertificateChain/ChainCertificate[last()]/@Id");
for (final XmlDom certToken : certificateChainXmlDom) {
final String certificateId = certToken.getValue("./@Id");
if (certificateId.equals(lastChainCertId) && trustedProspectiveCertificateChain) {
/**
* The trusted anchor is not checked. In the case of a certificate chain consisting of a single certificate
* which is trusted we need to set this variable to true.
*/
continue;
}
final XmlDom certificate = params.getCertificate(certificateId);
final String subContext = certificateId.equals(signingCertificateId) ? SIGNING_CERTIFICATE : CA_CERTIFICATE;
// certificate signature cryptographic constraints validation
if (!checkCertificateCryptographicConstraint(conclusion, certificate, contextName, subContext)) {
return conclusion;
}
}
// This validation process returns VALID
conclusion.setIndication(VALID);
return conclusion;
}
/**
* Preparation of information about revocation data and their freshness.
*
* @param revocationIssuingTimeString
* @return
*/
private boolean prepareRevocationFreshnessCheck(String revocationIssuingTimeString) {
boolean revocationFreshnessToBeChecked = constraintData.isRevocationFreshnessToBeChecked();
boolean revocationFresh = !revocationFreshnessToBeChecked;
if (revocationFreshnessToBeChecked && !revocationIssuingTimeString.isEmpty()) {
final Date revocationIssuingTime = DSSUtils.parseDate(revocationIssuingTimeString);
final long revocationDeltaTime = currentTime.getTime() - revocationIssuingTime.getTime();
if (revocationDeltaTime <= constraintData.getMaxRevocationFreshness()) {
revocationFresh = true;
}
}
return revocationFresh;
}
private boolean getBoolValue(final XmlDom xmlDom, final String xPath) {
return xmlDom == null ? false : xmlDom.getBoolValue(xPath);
}
private String getValue(final XmlDom xmlDom, final String xPath) {
return xmlDom == null ? "" : xmlDom.getValue(xPath);
}
/**
* This method checks that the current time is in the validity range of the certificate. If this constraint is not
* satisfied, abort the processing with the indication INDETERMINATE and the sub indication OUT_OF_BOUNDS_NO_POE.
*
* @param conclusion the conclusion to use to add the result of the check.
* @param context
* @param subContext
* @return false if the check failed and the process should stop, true otherwise.
*/
private boolean checkCertificateExpirationConstraint(final Conclusion conclusion, final String context, final String subContext) {
final CertificateExpirationConstraint constraint = constraintData.getSigningCertificateExpirationConstraint(context, subContext);
if (constraint == null) {
return true;
}
constraint.create(validationDataXmlNode, BBB_XCV_ICTIVRSC);
constraint.setCurrentTime(currentTime);
constraint.setNotAfter(getDate(signingCertificate, "./NotAfter"));
constraint.setNotBefore(getDate(signingCertificate, "./NotBefore"));
constraint.setExpiredCertsRevocationInfo(getDate(signingCertificate, "./TrustedServiceProvider/ExpiredCertsRevocationInfo"));
constraint.setIndications(INDETERMINATE, OUT_OF_BOUNDS_NO_POE, BBB_XCV_ICTIVRSC_ANS);
constraint.setConclusionReceiver(conclusion);
return constraint.check();
}
/**
* a) If no new chain can be built, abort the processing with the current status and the last chain built or, if
* no chain was built, with INDETERMINATE/NO_CERTIFICATE_CHAIN_FOUND.
*
* @param conclusion the conclusion to use to add the result of the check.
* @param trustedProspectiveCertificateChain
* @return false if the check failed and the process should stop, true otherwise.
*/
protected boolean checkProspectiveCertificateChainConstraint(final Conclusion conclusion, boolean trustedProspectiveCertificateChain) {
final Constraint constraint = constraintData.getProspectiveCertificateChainConstraint(contextName);
if (constraint == null) {
return true;
}
constraint.create(validationDataXmlNode, BBB_XCV_CCCBB);
constraint.setValue(trustedProspectiveCertificateChain);
constraint.setIndications(INDETERMINATE, NO_CERTIFICATE_CHAIN_FOUND, BBB_XCV_CCCBB_ANS);
constraint.setConclusionReceiver(conclusion);
return constraint.check();
}
protected boolean isTrustedProspectiveCertificateChain(final ProcessParameters params) {
final String lastChainCertId = contextElement.getValue("./CertificateChain/ChainCertificate[last()]/@Id");
final XmlDom lastChainCertificate = params.getCertificate(lastChainCertId);
boolean lastChainCertificateTrusted = false;
if (lastChainCertificate != null) {
lastChainCertificateTrusted = lastChainCertificate.getBoolValue("./Trusted/text()");
}
return lastChainCertificateTrusted;
}
/**
* Retrieves the {@code Date} from an {@code XmlNode} using the XPath query.
*
* @param xmlDom {@code XmlDom} containing the desired date.
* @param xPathQuery XPath query to run
* @return {@code Date} or null if the XPath query returns no element or if the date conversion is impossible.
*/
private Date getDate(final XmlDom xmlDom, final String xPathQuery) {
final String formatedDate = xmlDom.getValue(xPathQuery + "/text()");
try {
return DSSUtils.parseDate(formatedDate);
} catch (DSSException e) {
return null;
}
}
/**
* This method checks the signature of the given certificate.
*
* @param conclusion the conclusion to use to add the result of the check.
* @param certificateId
* @param certificateXmlDom
* @param subContext
* @return false if the check failed and the process should stop, true otherwise.
*/
private boolean checkCertificateSignatureConstraint(final Conclusion conclusion, final String certificateId, final XmlDom certificateXmlDom, final String subContext) {
final Constraint constraint = constraintData.getCertificateSignatureConstraint(contextName, subContext);
if (constraint == null) {
return true;
}
constraint.create(validationDataXmlNode, BBB_XCV_ICSI);
constraint.setValue(certificateXmlDom.getBoolValue(XP_SIGNATURE_VALID));
constraint.setIndications(INDETERMINATE, NO_CERTIFICATE_CHAIN_FOUND, BBB_XCV_ICSI_ANS);
constraint.setAttribute(CERTIFICATE_ID, certificateId);
constraint.setConclusionReceiver(conclusion);
return constraint.check();
}
/**
* This method checks the revocation data is available for the given certificate.
*
* @param conclusion the conclusion to use to add the result of the check.
* @param certificateId
* @param certificateXmlDom
* @param subContext
* @return false if the check failed and the process should stop, true otherwise.
*/
private boolean checkRevocationDataAvailableConstraint(final Conclusion conclusion, final String certificateId, final XmlDom certificateXmlDom, String subContext) {
final Constraint constraint = constraintData.getRevocationDataAvailableConstraint(contextName, subContext);
if (constraint == null) {
return true;
}
constraint.create(validationDataXmlNode, BBB_XCV_IRDPFC);
constraint.setValue(isRevocationDataAvailable(certificateXmlDom));
constraint.setIndications(INDETERMINATE, TRY_LATER, BBB_XCV_IRDPFC_ANS);
constraint.setAttribute(CERTIFICATE_ID, certificateId);
constraint.setConclusionReceiver(conclusion);
return constraint.check();
}
private String isRevocationDataAvailable(final XmlDom certificateXmlDom) {
final XmlDom revocation = certificateXmlDom.getElement("./Revocation");
return String.valueOf(revocation != null);
}
/**
* This method checks if the revocation data is trusted for the given certificate.
*
* @param conclusion the conclusion to use to add the result of the check.
* @param certificateId
* @param certificateXmlDom
* @param subContext
* @return false if the check failed and the process should stop, true otherwise.
*/
private boolean checkRevocationDataIsTrustedConstraint(Conclusion conclusion, String certificateId, XmlDom certificateXmlDom, String subContext) {
final Constraint constraint = constraintData.getRevocationDataIsTrustedConstraint(contextName, subContext);
if (constraint == null) {
return true;
}
constraint.create(validationDataXmlNode, BBB_XCV_IRDTFC);
final String anchorSource = certificateXmlDom.getValue("./Revocation/CertificateChain/ChainCertificate[last()]/Source/text()");
final CertificateSourceType anchorSourceType = DSSUtils.isBlank(anchorSource) ? CertificateSourceType.UNKNOWN : CertificateSourceType.valueOf(anchorSource);
constraint.setValue(isRevocationDataTrusted(anchorSourceType));
constraint.setIndications(INDETERMINATE, TRY_LATER, BBB_XCV_IRDTFC_ANS);
constraint.setAttribute(CERTIFICATE_ID, certificateId);
constraint.setAttribute(CERTIFICATE_SOURCE, anchorSource);
constraint.setConclusionReceiver(conclusion);
return constraint.check();
}
private String isRevocationDataTrusted(final CertificateSourceType anchorSourceType) {
final boolean trusted = CertificateSourceType.TRUSTED_LIST.equals(anchorSourceType) || CertificateSourceType.TRUSTED_STORE.equals(anchorSourceType);
return String.valueOf(trusted);
}
/**
* This method checks if the revocation data is fresh for the given certificate. If the revocation data does not exist then this check is ignored.
*
* @param conclusion the conclusion to use to add the result of the check.
* @param certificateId
* @param revocationFresh
* @param revocationNextUpdate
* @param revocationIssuingTimeString
* @param subContext
* @return false if the check failed and the process should stop, true otherwise.
*/
private boolean checkRevocationFreshnessConstraint(final Conclusion conclusion, final String certificateId, final boolean revocationFresh, final String revocationNextUpdate,
final String revocationIssuingTimeString, String subContext) {
// If the revocation data does not exist then this check is ignored.
if (DSSUtils.isBlank(revocationIssuingTimeString)) {
return true;
}
final Constraint constraint = constraintData.getRevocationDataFreshnessConstraint(contextName, subContext);
if (constraint == null) {
return true;
}
constraint.create(validationDataXmlNode, BBB_XCV_IRIF);
constraint.setValue(String.valueOf(revocationFresh));
constraint.setIndications(INDETERMINATE, TRY_LATER, BBB_XCV_IRIF_ANS);
constraint.setAttribute(CERTIFICATE_ID, certificateId);
constraint.setAttribute(REVOCATION_NEXT_UPDATE, revocationNextUpdate);
constraint.setAttribute(REVOCATION_ISSUING_TIME, revocationIssuingTimeString);
constraint.setAttribute(MAXIMUM_REVOCATION_FRESHNESS, constraintData.getFormatedMaxRevocationFreshness());
constraint.setConclusionReceiver(conclusion);
return constraint.check();
}
/**
* This method checks if the signing certificate has an appropriate key usage.
*
* @param conclusion the conclusion to use to add the result of the check.
* @param certificateId
* @param certificateXmlDom
* @return
*/
private boolean checkKeyUsageConstraint(Conclusion conclusion, String certificateId, XmlDom certificateXmlDom) {
final Constraint constraint = constraintData.getSigningCertificateKeyUsageConstraint(contextName);
if (constraint == null) {
return true;
}
constraint.create(validationDataXmlNode, BBB_XCV_ISCGKU);
final List<XmlDom> keyUsageBits = certificateXmlDom.getElements("./KeyUsageBits/KeyUsage");
final List<String> stringList = XmlDom.convertToStringList(keyUsageBits);
constraint.setValue(stringList);
constraint.setIndications(INVALID, SIG_CONSTRAINTS_FAILURE, BBB_XCV_ISCGKU_ANS);
constraint.setAttribute(CERTIFICATE_ID, certificateId);
constraint.setConclusionReceiver(conclusion);
return constraint.checkInList();
}
/**
* This method checks if the signing certificate is revoked.
* <p/>
* c) If the certificate path validation returns a failure indication because the signer's certificate has
* been determined to be revoked, abort the process with the indication INDETERMINATE, the sub indication
* REVOKED_NO_POE, the validated chain, the revocation date and the reason for revocation.
*
* @param conclusion the conclusion to use to add the result of the check.
* @param certificateId
* @param revocationStatus
* @param revocationReason
* @param revocationDatetime @return false if the check failed and the process should stop, true otherwise.
* @param subContext
*/
private boolean checkSigningCertificateRevokedConstraint(final Conclusion conclusion, final String certificateId, boolean revocationStatus, final String revocationReason,
final String revocationDatetime, String subContext) {
final Constraint constraint = constraintData.getSigningCertificateRevokedConstraint(contextName, subContext);
if (constraint == null) {
return true;
}
constraint.create(validationDataXmlNode, BBB_XCV_ISCR);
final boolean revoked = !revocationStatus && !revocationReason.equals(CRL_REASON_CERTIFICATE_HOLD);
constraint.setValue(String.valueOf(revoked));
constraint.setIndications(INDETERMINATE, REVOKED_NO_POE, BBB_XCV_ISCR_ANS);
constraint.setAttribute(CERTIFICATE_ID, certificateId);
if (DSSUtils.isNotBlank(revocationDatetime)) {
constraint.setAttribute(REVOCATION_TIME, revocationDatetime);
}
if (DSSUtils.isNotBlank(revocationReason)) {
constraint.setAttribute(REVOCATION_REASON, revocationReason);
}
constraint.setConclusionReceiver(conclusion);
return constraint.check();
}
/**
* This method checks if the signing certificate is on hold.
* <p/>
* d) If the certificate path validation returns a failure indication because the signer's certificate has
* been determined to be on hold, abort the process with the indication INDETERMINATE, the sub indication
* TRY_LATER, the suspension time and, if available, the content of the NEXT_UPDATE-field of the CRL used as
* the suggestion for when to try the validation again.
*
* @param conclusion the conclusion to use to add the result of the check.
* @param certificateId
* @param revocationStatus
* @param revocationReason
* @param revocationDatetime
* @param revocationNextUpdate
* @param subContext
* @return
*/
private boolean checkSigningCertificateOnHoldConstraint(final Conclusion conclusion, final String certificateId, final boolean revocationStatus, final String revocationReason,
final String revocationDatetime, final String revocationNextUpdate, String subContext) {
final Constraint constraint = constraintData.getSigningCertificateOnHoldConstraint(contextName, subContext);
if (constraint == null) {
return true;
}
constraint.create(validationDataXmlNode, BBB_XCV_ISCOH);
final boolean onHold = !revocationStatus && revocationReason.equals(CRL_REASON_CERTIFICATE_HOLD);
constraint.setValue(String.valueOf(onHold));
constraint.setIndications(INDETERMINATE, TRY_LATER, BBB_XCV_ISCOH_ANS);
constraint.setAttribute(CERTIFICATE_ID, certificateId);
if (DSSUtils.isNotBlank(revocationDatetime)) {
constraint.setAttribute(REVOCATION_TIME, revocationDatetime);
}
if (DSSUtils.isNotBlank(revocationReason)) {
constraint.setAttribute(REVOCATION_NEXT_UPDATE, revocationNextUpdate);
}
constraint.setConclusionReceiver(conclusion);
return constraint.check();
}
/**
* This method checks if the TSL validity is in concordance with the signing certificate .
*
* @param conclusion the conclusion to use to add the result of the check.
* @param certificateId
* @param certificateXmlDom @return
*/
private boolean checkSigningCertificateTSLValidityConstraint(final Conclusion conclusion, String certificateId, final XmlDom certificateXmlDom) {
final String trustedSource = certificateXmlDom.getValue("./CertificateChain/ChainCertificate[last()]/Source/text()");
if (CertificateSourceType.TRUSTED_STORE.name().equals(trustedSource)) {
return true;
}
final Constraint constraint = constraintData.getSigningCertificateTSLValidityConstraint(contextName);
if (constraint == null) {
return true;
}
constraint.create(validationDataXmlNode, CTS_IIDOCWVPOTS);
final Date certificateValidFrom = certificateXmlDom.getTimeValueOrNull("./NotBefore/text()");
final List<XmlDom> tspList = certificateXmlDom.getElements("./TrustedServiceProvider");
boolean found = false;
for (final XmlDom trustedServiceProviderXmlDom : tspList) {
final String serviceTypeIdentifier = trustedServiceProviderXmlDom.getValue("./TSPServiceType/text()");
if (!TSLConstant.CA_QC.equals(serviceTypeIdentifier)) {
continue;
}
final Date statusStartDate = trustedServiceProviderXmlDom.getTimeValueOrNull("./StartDate/text()");
final Date statusEndDate = trustedServiceProviderXmlDom.getTimeValueOrNull("./EndDate/text()");
// The issuing time of the certificate should be into the validity period of the associated service
if (certificateValidFrom.after(statusStartDate) && (statusEndDate == null || certificateValidFrom.before(statusEndDate))) {
found = true;
}
}
constraint.setValue(found);
constraint.setIndications(INDETERMINATE, TRY_LATER, CTS_IIDOCWVPOTS_ANS);
constraint.setAttribute(CERTIFICATE_ID, certificateId);
constraint.setConclusionReceiver(conclusion);
return constraint.check();
}
/**
* This method checks if the TSL status of the signing certificate.
*
* @param conclusion the conclusion to use to add the result of the check.
* @param certificateId
* @param certificateXmlDom @return
*/
private boolean checkSigningCertificateTSLStatusConstraint(final Conclusion conclusion, String certificateId, final XmlDom certificateXmlDom) {
final String trustedSource = certificateXmlDom.getValue("./CertificateChain/ChainCertificate[last()]/Source/text()");
if (CertificateSourceType.TRUSTED_STORE.name().equals(trustedSource)) {
return true;
}
final Constraint constraint = constraintData.getSigningCertificateTSLStatusConstraint(contextName);
if (constraint == null) {
return true;
}
constraint.create(validationDataXmlNode, CTS_WITSS);
final List<XmlDom> tspList = certificateXmlDom.getElements("./TrustedServiceProvider");
boolean acceptableStatus = false;
String status = DSSUtils.EMPTY;
for (final XmlDom trustedServiceProviderXmlDom : tspList) {
status = trustedServiceProviderXmlDom == null ? "" : trustedServiceProviderXmlDom.getValue("./Status/text()");
acceptableStatus = SERVICE_STATUS_UNDERSUPERVISION.equals(status) || SERVICE_STATUS_SUPERVISIONINCESSATION.equals(status) || SERVICE_STATUS_ACCREDITED
.equals(status) || SERVICE_STATUS_UNDERSUPERVISION_119612.equals(status) || SERVICE_STATUS_SUPERVISIONINCESSATION_119612
.equals(status) || SERVICE_STATUS_ACCREDITED_119612.equals(status);
if (acceptableStatus) {
break;
}
}
constraint.setValue(acceptableStatus);
constraint.setIndications(INDETERMINATE, TRY_LATER, CTS_WITSS_ANS);
constraint.setAttribute(CERTIFICATE_ID, certificateId);
constraint.setAttribute(TRUSTED_SERVICE_STATUS, status);
constraint.setConclusionReceiver(conclusion);
return constraint.check();
}
/**
* This method checks if the TSL status of the signing certificate.
*
* @param conclusion the conclusion to use to add the result of the check.
* @param certificateId
* @param certificateXmlDom @return
*/
private boolean checkSigningCertificateTSLStatusAndValidityConstraint(final Conclusion conclusion, String certificateId, final XmlDom certificateXmlDom) {
final String trustedSource = certificateXmlDom.getValue("./CertificateChain/ChainCertificate[last()]/Source/text()");
if (CertificateSourceType.TRUSTED_STORE.name().equals(trustedSource)) {
return true;
}
final Constraint constraint = constraintData.getSigningCertificateTSLStatusAndValidityConstraint(contextName);
if (constraint == null) {
return true;
}
constraint.create(validationDataXmlNode, CTS_ITACBT);
final Date certificateValidFrom = certificateXmlDom.getTimeValueOrNull("./NotBefore/text()");
final List<XmlDom> tspList = certificateXmlDom.getElements("./TrustedServiceProvider");
boolean found = false;
for (final XmlDom trustedServiceProviderXmlDom : tspList) {
final String serviceTypeIdentifier = trustedServiceProviderXmlDom.getValue("./TSPServiceType/text()");
if (!TSLConstant.CA_QC.equals(serviceTypeIdentifier)) {
continue;
}
final Date statusStartDate = trustedServiceProviderXmlDom.getTimeValueOrNull("./StartDate/text()");
final Date statusEndDate = trustedServiceProviderXmlDom.getTimeValueOrNull("./EndDate/text()");
if (certificateValidFrom.after(statusStartDate) && (statusEndDate == null || certificateValidFrom.before(statusEndDate))) {
final String status = trustedServiceProviderXmlDom == null ? "" : trustedServiceProviderXmlDom.getValue("./Status/text()");
found = SERVICE_STATUS_UNDERSUPERVISION.equals(status) || SERVICE_STATUS_SUPERVISIONINCESSATION.equals(status) || SERVICE_STATUS_ACCREDITED
.equals(status) || SERVICE_STATUS_UNDERSUPERVISION_119612.equals(status) || SERVICE_STATUS_SUPERVISIONINCESSATION_119612
.equals(status) || SERVICE_STATUS_ACCREDITED_119612.equals(status);
if (found) {
break;
}
}
}
constraint.setValue(found);
constraint.setIndications(INDETERMINATE, TRY_LATER, CTS_ITACBT_ANS);
constraint.setAttribute(CERTIFICATE_ID, certificateId);
constraint.setConclusionReceiver(conclusion);
return constraint.check();
}
/**
* This method checks if the intermediate certificate is revoked.
* <p/>
* e) If the certificate path validation returns a failure indication because an intermediate CA has been
* determined to be revoked, set the current status to INDETERMINATE/REVOKED_CA_NO_POE and go to step 2.
*
* @param conclusion the conclusion to use to add the result of the check.
* @param certificateId
* @param revocationStatus
* @param revocationReason
* @param revocationDatetime @return false if the check failed and the process should stop, true otherwise.
* @param subContext
*/
private boolean checkIntermediateCertificateRevokedConstraint(final Conclusion conclusion, final String certificateId, final boolean revocationStatus,
final String revocationReason, final String revocationDatetime, String subContext) {
final Constraint constraint = constraintData.getIntermediateCertificateRevokedConstraint(contextName);
if (constraint == null) {
return true;
}
constraint.create(validationDataXmlNode, BBB_XCV_IICR, certificateId);
final boolean revoked = !revocationStatus;
constraint.setValue(String.valueOf(revoked));
constraint.setIndications(INDETERMINATE, REVOKED_CA_NO_POE, BBB_XCV_IICR_ANS);
constraint.setAttribute(CERTIFICATE_ID, certificateId);
if (DSSUtils.isNotBlank(revocationDatetime)) {
constraint.setAttribute(REVOCATION_TIME, revocationDatetime);
}
if (DSSUtils.isNotBlank(revocationReason)) {
constraint.setAttribute(REVOCATION_REASON, revocationReason);
}
constraint.setConclusionReceiver(conclusion);
return constraint.check();
}
/**
* 4) Apply the Chain Constraints to the chain. Certificate meta-data shall be taken into account when checking
* these constraints against the chain. If the chain does not match these constraints, set the current status to
* INVALID/CHAIN_CONSTRAINTS_FAILURE and go to step 2.
*
* @param conclusion the conclusion to use to add the result of the check.
*/
private boolean checkChainConstraint(final Conclusion conclusion) {
final Constraint constraint = constraintData.getChainConstraint();
if (constraint == null) {
return true;
}
constraint.create(validationDataXmlNode, BBB_XCV_ACCM);
// TODO: (Bob: 2014 Mar 09) --> DSS does not check these constraints
constraint.setValue("TO BE IMPLEMENTED");
constraint.setIndications(INVALID, CHAIN_CONSTRAINTS_FAILURE, EMPTY);
constraint.setConclusionReceiver(conclusion);
return constraint.check();
}
/**
* Mandates the signer's certificate used in validating the signature to be a qualified certificate as defined
* in Directive 1999/93/EC [9]. This status can be derived from:
*
* @param conclusion the conclusion to use to add the result of the check.
* @param qualifiedCertificate indicates if the signing certificate is qualified.
*/
protected boolean checkSigningCertificateQualificationConstraint(final Conclusion conclusion, final boolean qualifiedCertificate) {
final Constraint constraint = constraintData.getSigningCertificateQualificationConstraint();
if (constraint == null) {
return true;
}
constraint.create(validationDataXmlNode, BBB_XCV_CMDCIQC);
constraint.setValue(String.valueOf(qualifiedCertificate));
constraint.setIndications(INVALID, CHAIN_CONSTRAINTS_FAILURE, BBB_XCV_CMDCIQC_ANS);
constraint.setConclusionReceiver(conclusion);
return constraint.check();
}
/**
* Mandates the end user certificate used in validating the signature to be supported by a secure signature
* creation device (SSCD) as defined in Directive 1999/93/EC [9].
*
* @param conclusion the conclusion to use to add the result of the check.
* @param supportedBySSCD indicates if the signing certificate is qualified.
*/
protected boolean checkSigningCertificateSupportedBySSCDConstraint(final Conclusion conclusion, final boolean supportedBySSCD) {
final Constraint constraint = constraintData.getSigningCertificateSupportedBySSCDConstraint();
if (constraint == null) {
return true;
}
constraint.create(validationDataXmlNode, BBB_XCV_CMDCISSCD);
constraint.setValue(String.valueOf(supportedBySSCD));
constraint.setIndications(INVALID, CHAIN_CONSTRAINTS_FAILURE, BBB_XCV_CMDCISSCD_ANS);
constraint.setConclusionReceiver(conclusion);
return constraint.check();
}
/**
* Mandates the signer's certificate used in validating the signature to be issued by a certificate authority
* issuing certificate as having been issued to a legal person.
*
* @param conclusion the conclusion to use to add the result of the check.
* @param issuedToLegalPerson indicates if the signing certificate is qualified.
*/
protected boolean checkSigningCertificateIssuedToLegalPersonConstraint(final Conclusion conclusion, final boolean issuedToLegalPerson) {
final Constraint constraint = constraintData.getSigningCertificateIssuedToLegalPersonConstraint();
if (constraint == null) {
return true;
}
constraint.create(validationDataXmlNode, BBB_XCV_CMDCIITLP);
constraint.setValue(String.valueOf(issuedToLegalPerson));
constraint.setIndications(INVALID, CHAIN_CONSTRAINTS_FAILURE, BBB_XCV_CMDCIITLP_ANS);
constraint.setConclusionReceiver(conclusion);
return constraint.check();
}
/**
* Check of: main signature cryptographic verification
*
* @param conclusion the conclusion to use to add the result of the check.
* @param context
* @param subContext @return false if the check failed and the process should stop, true otherwise.
*/
private boolean checkCertificateCryptographicConstraint(final Conclusion conclusion, final XmlDom contextXmlDom, String context, String subContext) {
if (contextXmlDom == null) {
return true;
}
final SignatureCryptographicConstraint constraint = constraintData.getSignatureCryptographicConstraint(context, subContext);
if (constraint == null) {
return true;
}
constraint.create(validationDataXmlNode, ASCCM);
constraint.setCurrentTime(currentTime);
constraint.setEncryptionAlgorithm(getValue(contextXmlDom, XP_ENCRYPTION_ALGO_USED_TO_SIGN_THIS_TOKEN));
constraint.setDigestAlgorithm(getValue(contextXmlDom, XP_DIGEST_ALGO_USED_TO_SIGN_THIS_TOKEN));
constraint.setKeyLength(getValue(contextXmlDom, XP_KEY_LENGTH_USED_TO_SIGN_THIS_TOKEN));
constraint.setIndications(INDETERMINATE, CRYPTO_CONSTRAINTS_FAILURE_NO_POE, EMPTY);
constraint.setConclusionReceiver(conclusion);
return constraint.check();
}
}