/* * 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(); } }