{"record":{"id":"25630f16ac22415b","repo":"apereo/cas","slug":"crl-data-expired-on","errorCode":null,"errorMessage":"CRL data expired on [{}]","messagePattern":"CRL data expired on \\[(.+?)\\]","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"support/cas-server-support-x509-core/src/main/java/org/apereo/cas/adaptors/x509/authentication/revocation/checker/AbstractCRLRevocationChecker.java","lineNumber":72,"sourceCode":"        this.checkAll = checkAll;\n        this.unavailableCRLPolicy = Objects.requireNonNullElseGet(unavailableCRLPolicy, DenyRevocationPolicy::new);\n        this.expiredCRLPolicy = Objects.requireNonNullElseGet(expiredCRLPolicy, () -> new ThresholdExpiredCRLRevocationPolicy(0));\n    }\n\n    @Override\n    public void check(@NonNull final X509Certificate cert) throws GeneralSecurityException {\n        LOGGER.debug(\"Evaluating certificate revocation status for [{}]\", CertUtils.toString(cert));\n        val crls = getCRLs(cert);\n\n        if (crls == null || crls.isEmpty()) {\n            LOGGER.warn(\"CRL data is not available for [{}]\", CertUtils.toString(cert));\n            this.unavailableCRLPolicy.apply(null);\n            return;\n        }\n\n        val expiredCrls = new ArrayList<X509CRL>(crls.size());\n        crls.stream().filter(CertUtils::isExpired).forEach(crl -> {\n            LOGGER.warn(\"CRL data expired on [{}]\", crl.getNextUpdate());\n            expiredCrls.add(crl);\n        });\n\n        if (crls.size() == expiredCrls.size()) {\n            LOGGER.warn(\"All CRLs retrieved have expired. Applying CRL expiration policy...\");\n            for (val crl : expiredCrls) {\n                this.expiredCRLPolicy.apply(crl);\n            }\n        } else {\n            crls.removeAll(expiredCrls);\n            LOGGER.debug(\"Valid CRLs [{}] found that are not expired yet\", crls);\n\n            val revokedCrls = crls.stream().map(crl -> crl.getRevokedCertificate(cert)).filter(Objects::nonNull).toList();\n            if (revokedCrls.size() == crls.size()) {\n                val entry = revokedCrls.getFirst();\n                LOGGER.warn(\"All CRL entries have been revoked. Rejecting the first entry [{}]\", entry);\n                throw new RevokedCertificateException(entry);\n            }","sourceCodeStart":54,"sourceCodeEnd":90,"githubUrl":"https://github.com/apereo/cas/blob/e7288fc434b4f4505b8452e1a57e8fb3111bb863/support/cas-server-support-x509-core/src/main/java/org/apereo/cas/adaptors/x509/authentication/revocation/checker/AbstractCRLRevocationChecker.java#L54-L90","documentation":"Logged when one or more retrieved CRLs have passed their nextUpdate timestamp (CertUtils.isExpired). Each expired CRL is collected and reported with its nextUpdate date. Expiration itself is only a warning here; if ALL CRLs are expired, the expiredCRLPolicy is applied (see the next message).","triggerScenarios":"AbstractCRLRevocationChecker.check() fetches CRLs whose thisUpdate/nextUpdate window has lapsed — i.e. the CA has not republished the CRL since nextUpdate passed.","commonSituations":"Stale CRL files cached on disk or in an HTTP cache past their validity window; an offline/broken CA that stopped publishing CRL updates; clock skew on the CAS server making valid CRLs appear expired.","solutions":["Refresh the CRL: re-download from the CA's distribution point or update the local resource file.","Check server clock/NTP sync ('timedatectl') to rule out skew.","Configure CRL caching with a TTL shorter than the CA's CRL publish interval.","Adjust the expired-CRL policy (allow/deny) if expired-but-usable CRLs should be tolerated, understanding the security tradeoff."],"exampleFix":"// before\ncas.authn.x509.crl.resource.location=file:/etc/cas/crls/ca.crl\n// after\n# automate refresh (cron/systemd timer) so nextUpdate never lapses\n0 * * * * curl -s https://crl.example.com/ca.crl -o /etc/cas/crls/ca.crl","handlingStrategy":"retry","validationCode":"X509CRL crl = /* fetched */;\nif (crl.getNextUpdate() != null && crl.getNextUpdate().toInstant().isBefore(java.time.Instant.now())) {\n    // refresh CRL before calling check()\n}","typeGuard":"boolean isStale = Optional.ofNullable(crl.getNextUpdate())\n        .map(d -> d.toInstant().isBefore(Instant.now()))\n        .orElse(true);","tryCatchPattern":"try {\n    checker.check(cert);\n} catch (GeneralSecurityException e) {\n    // expired-CRL policy (e.g. DENY) rejected the request\n    LOGGER.warn(\"CRL expired; attempt refresh and retry once\", e);\n}","preventionTips":["Schedule CRL refresh (cron/systemd timer) faster than the CA's publish interval.","Use HTTP cache headers or a fetcher that honors nextUpdate.","Keep server clocks synchronized via NTP.","Alert when a cached CRL approaches its nextUpdate deadline."],"tags":["x509","crl","expired","revocation"],"backgroundTag":"crl-expired","analyzedSha":"e7288fc434b4f4505b8452e1a57e8fb3111bb863","analyzedAt":"2026-09-08T15:39:16.015Z","contentChangedAt":"2026-09-08T15:39:16.015Z","schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}