/**
   * Checks a distribution point for revocation information for the certificate <code>attrCert
   * </code>.
   *
   * @param dp The distribution point to consider.
   * @param attrCert The attribute certificate which should be checked.
   * @param paramsPKIX PKIX parameters.
   * @param validDate The date when the certificate revocation status should be checked.
   * @param issuerCert Certificate to check if it is revoked.
   * @param reasonMask The reasons mask which is already checked.
   * @param certPathCerts The certificates of the certification path to be checked.
   * @throws AnnotatedException if the certificate is revoked or the status cannot be checked or
   *     some error occurs.
   */
  private static void checkCRL(
      DistributionPoint dp,
      X509AttributeCertificate attrCert,
      ExtendedPKIXParameters paramsPKIX,
      Date validDate,
      X509Certificate issuerCert,
      CertStatus certStatus,
      ReasonsMask reasonMask,
      List certPathCerts)
      throws AnnotatedException {

    /*
     * 4.3.6 No Revocation Available
     *
     * The noRevAvail extension, defined in [X.509-2000], allows an AC
     * issuer to indicate that no revocation information will be made
     * available for this AC.
     */
    if (attrCert.getExtensionValue(X509Extensions.NoRevAvail.getId()) != null) {
      return;
    }
    Date currentDate = new Date(System.currentTimeMillis());
    if (validDate.getTime() > currentDate.getTime()) {
      throw new AnnotatedException("Validation time is in future.");
    }

    // (a)
    /*
     * We always get timely valid CRLs, so there is no step (a) (1).
     * "locally cached" CRLs are assumed to be in getStore(), additional
     * CRLs must be enabled in the ExtendedPKIXParameters and are in
     * getAdditionalStore()
     */

    Set crls = CertPathValidatorUtilities.getCompleteCRLs(dp, attrCert, currentDate, paramsPKIX);
    boolean validCrlFound = false;
    AnnotatedException lastException = null;
    Iterator crl_iter = crls.iterator();

    while (crl_iter.hasNext()
        && certStatus.getCertStatus() == CertStatus.UNREVOKED
        && !reasonMask.isAllReasons()) {
      try {
        X509CRL crl = (X509CRL) crl_iter.next();

        // (d)
        ReasonsMask interimReasonsMask = RFC3280CertPathUtilities.processCRLD(crl, dp);

        // (e)
        /*
         * The reasons mask is updated at the end, so only valid CRLs
         * can update it. If this CRL does not contain new reasons it
         * must be ignored.
         */
        if (!interimReasonsMask.hasNewReasons(reasonMask)) {
          continue;
        }

        // (f)
        Set keys =
            RFC3280CertPathUtilities.processCRLF(
                crl, attrCert, null, null, paramsPKIX, certPathCerts);
        // (g)
        PublicKey key = RFC3280CertPathUtilities.processCRLG(crl, keys);

        X509CRL deltaCRL = null;

        if (paramsPKIX.isUseDeltasEnabled()) {
          // get delta CRLs
          Set deltaCRLs = CertPathValidatorUtilities.getDeltaCRLs(currentDate, paramsPKIX, crl);
          // we only want one valid delta CRL
          // (h)
          deltaCRL = RFC3280CertPathUtilities.processCRLH(deltaCRLs, key);
        }

        /*
         * CRL must be be valid at the current time, not the validation
         * time. If a certificate is revoked with reason keyCompromise,
         * cACompromise, it can be used for forgery, also for the past.
         * This reason may not be contained in older CRLs.
         */

        /*
         * in the chain model signatures stay valid also after the
         * certificate has been expired, so they do not have to be in
         * the CRL vality time
         */

        if (paramsPKIX.getValidityModel() != ExtendedPKIXParameters.CHAIN_VALIDITY_MODEL) {
          /*
           * if a certificate has expired, but was revoked, it is not
           * more in the CRL, so it would be regarded as valid if the
           * first check is not done
           */
          if (attrCert.getNotAfter().getTime() < crl.getThisUpdate().getTime()) {
            throw new AnnotatedException("No valid CRL for current time found.");
          }
        }

        RFC3280CertPathUtilities.processCRLB1(dp, attrCert, crl);

        // (b) (2)
        RFC3280CertPathUtilities.processCRLB2(dp, attrCert, crl);

        // (c)
        RFC3280CertPathUtilities.processCRLC(deltaCRL, crl, paramsPKIX);

        // (i)
        RFC3280CertPathUtilities.processCRLI(validDate, deltaCRL, attrCert, certStatus, paramsPKIX);

        // (j)
        RFC3280CertPathUtilities.processCRLJ(validDate, crl, attrCert, certStatus);

        // (k)
        if (certStatus.getCertStatus() == CRLReason.removeFromCRL) {
          certStatus.setCertStatus(CertStatus.UNREVOKED);
        }

        // update reasons mask
        reasonMask.addReasons(interimReasonsMask);
        validCrlFound = true;
      } catch (AnnotatedException e) {
        lastException = e;
      }
    }
    if (!validCrlFound) {
      throw lastException;
    }
  }
  protected static void getCertStatus(
      Date validDate, X509CRL crl, Object cert, CertStatus certStatus) throws AnnotatedException {
    X509CRLEntry crl_entry = null;

    boolean isIndirect;
    try {
      isIndirect = X509CRLObject.isIndirectCRL(crl);
    } catch (CRLException exception) {
      throw new AnnotatedException("Failed check for indirect CRL.", exception);
    }

    if (isIndirect) {
      crl_entry = crl.getRevokedCertificate(getSerialNumber(cert));

      if (crl_entry == null) {
        return;
      }

      X500Principal certIssuer = crl_entry.getCertificateIssuer();

      if (certIssuer == null) {
        certIssuer = getIssuerPrincipal(crl);
      }

      if (!getEncodedIssuerPrincipal(cert).equals(certIssuer)) {
        return;
      }
    } else if (!getEncodedIssuerPrincipal(cert).equals(getIssuerPrincipal(crl))) {
      return; // not for our issuer, ignore
    } else {
      crl_entry = crl.getRevokedCertificate(getSerialNumber(cert));

      if (crl_entry == null) {
        return;
      }
    }

    DEREnumerated reasonCode = null;
    if (crl_entry.hasExtensions()) {
      try {
        reasonCode =
            DEREnumerated.getInstance(
                CertPathValidatorUtilities.getExtensionValue(
                    crl_entry, X509Extension.reasonCode.getId()));
      } catch (Exception e) {
        throw new AnnotatedException("Reason code CRL entry extension could not be decoded.", e);
      }
    }

    // for reason keyCompromise, caCompromise, aACompromise or
    // unspecified
    if (!(validDate.getTime() < crl_entry.getRevocationDate().getTime())
        || reasonCode == null
        || reasonCode.getValue().intValue() == 0
        || reasonCode.getValue().intValue() == 1
        || reasonCode.getValue().intValue() == 2
        || reasonCode.getValue().intValue() == 8) {

      // (i) or (j) (1)
      if (reasonCode != null) {
        certStatus.setCertStatus(reasonCode.getValue().intValue());
      }
      // (i) or (j) (2)
      else {
        certStatus.setCertStatus(CRLReason.unspecified);
      }
      certStatus.setRevocationDate(crl_entry.getRevocationDate());
    }
  }
  /**
   * Checks if an attribute certificate is revoked.
   *
   * @param attrCert Attribute certificate to check if it is revoked.
   * @param paramsPKIX PKIX parameters.
   * @param issuerCert The issuer certificate of the attribute certificate <code>attrCert</code>.
   * @param validDate The date when the certificate revocation status should be checked.
   * @param certPathCerts The certificates of the certification path to be checked.
   * @throws CertPathValidatorException if the certificate is revoked or the status cannot be
   *     checked or some error occurs.
   */
  protected static void checkCRLs(
      X509AttributeCertificate attrCert,
      ExtendedPKIXParameters paramsPKIX,
      X509Certificate issuerCert,
      Date validDate,
      List certPathCerts)
      throws CertPathValidatorException {
    if (paramsPKIX.isRevocationEnabled()) {
      // check if revocation is available
      if (attrCert.getExtensionValue(NO_REV_AVAIL) == null) {
        CRLDistPoint crldp = null;
        try {
          crldp =
              CRLDistPoint.getInstance(
                  CertPathValidatorUtilities.getExtensionValue(attrCert, CRL_DISTRIBUTION_POINTS));
        } catch (AnnotatedException e) {
          throw new CertPathValidatorException(
              "CRL distribution point extension could not be read.", e);
        }
        try {
          CertPathValidatorUtilities.addAdditionalStoresFromCRLDistributionPoint(crldp, paramsPKIX);
        } catch (AnnotatedException e) {
          throw new CertPathValidatorException(
              "No additional CRL locations could be decoded from CRL distribution point extension.",
              e);
        }
        CertStatus certStatus = new CertStatus();
        ReasonsMask reasonsMask = new ReasonsMask();

        AnnotatedException lastException = null;
        boolean validCrlFound = false;
        // for each distribution point
        if (crldp != null) {
          DistributionPoint dps[] = null;
          try {
            dps = crldp.getDistributionPoints();
          } catch (Exception e) {
            throw new ExtCertPathValidatorException("Distribution points could not be read.", e);
          }
          try {
            for (int i = 0;
                i < dps.length
                    && certStatus.getCertStatus() == CertStatus.UNREVOKED
                    && !reasonsMask.isAllReasons();
                i++) {
              ExtendedPKIXParameters paramsPKIXClone = (ExtendedPKIXParameters) paramsPKIX.clone();
              checkCRL(
                  dps[i],
                  attrCert,
                  paramsPKIXClone,
                  validDate,
                  issuerCert,
                  certStatus,
                  reasonsMask,
                  certPathCerts);
              validCrlFound = true;
            }
          } catch (AnnotatedException e) {
            lastException = new AnnotatedException("No valid CRL for distribution point found.", e);
          }
        }

        /*
         * If the revocation status has not been determined, repeat the
         * process above with any available CRLs not specified in a
         * distribution point but issued by the certificate issuer.
         */

        if (certStatus.getCertStatus() == CertStatus.UNREVOKED && !reasonsMask.isAllReasons()) {
          try {
            /*
             * assume a DP with both the reasons and the cRLIssuer
             * fields omitted and a distribution point name of the
             * certificate issuer.
             */
            DERObject issuer = null;
            try {

              issuer =
                  new ASN1InputStream(
                          ((X500Principal) attrCert.getIssuer().getPrincipals()[0]).getEncoded())
                      .readObject();
            } catch (Exception e) {
              throw new AnnotatedException(
                  "Issuer from certificate for CRL could not be reencoded.", e);
            }
            DistributionPoint dp =
                new DistributionPoint(
                    new DistributionPointName(
                        0, new GeneralNames(new GeneralName(GeneralName.directoryName, issuer))),
                    null,
                    null);
            ExtendedPKIXParameters paramsPKIXClone = (ExtendedPKIXParameters) paramsPKIX.clone();
            checkCRL(
                dp,
                attrCert,
                paramsPKIXClone,
                validDate,
                issuerCert,
                certStatus,
                reasonsMask,
                certPathCerts);
            validCrlFound = true;
          } catch (AnnotatedException e) {
            lastException = new AnnotatedException("No valid CRL for distribution point found.", e);
          }
        }

        if (!validCrlFound) {
          throw new ExtCertPathValidatorException("No valid CRL found.", lastException);
        }
        if (certStatus.getCertStatus() != CertStatus.UNREVOKED) {
          String message =
              "Attribute certificate revocation after " + certStatus.getRevocationDate();
          message += ", reason: " + RFC3280CertPathUtilities.crlReasons[certStatus.getCertStatus()];
          throw new CertPathValidatorException(message);
        }
        if (!reasonsMask.isAllReasons() && certStatus.getCertStatus() == CertStatus.UNREVOKED) {
          certStatus.setCertStatus(CertStatus.UNDETERMINED);
        }
        if (certStatus.getCertStatus() == CertStatus.UNDETERMINED) {
          throw new CertPathValidatorException(
              "Attribute certificate status could not be determined.");
        }

      } else {
        if (attrCert.getExtensionValue(CRL_DISTRIBUTION_POINTS) != null
            || attrCert.getExtensionValue(AUTHORITY_INFO_ACCESS) != null) {
          throw new CertPathValidatorException(
              "No rev avail extension is set, but also an AC revocation pointer.");
        }
      }
    }
  }