package org.apereo.cas.adaptors.x509.authentication.revocation.checker; import org.apereo.cas.adaptors.x509.authentication.revocation.RevokedCertificateException; import org.apereo.cas.adaptors.x509.authentication.revocation.policy.DenyRevocationPolicy; import org.apereo.cas.adaptors.x509.authentication.revocation.policy.RevocationPolicy; import org.apereo.cas.adaptors.x509.authentication.revocation.policy.ThresholdExpiredCRLRevocationPolicy; import org.apereo.cas.adaptors.x509.util.CertUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.security.GeneralSecurityException; import java.security.cert.X509CRL; import java.security.cert.X509CRLEntry; import java.security.cert.X509Certificate; import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.Objects; import java.util.stream.Collectors; /** * Base class for all CRL-based revocation checkers. * * @author Marvin S. Addison * @since 3.4.6 */ public abstract class AbstractCRLRevocationChecker implements RevocationChecker { private static final Logger LOGGER = LoggerFactory.getLogger(AbstractCRLRevocationChecker.class); /** * Flag to indicate whether all * crls should be checked for the cert resource. * Defaults to {@code false}. **/ protected final boolean checkAll; /** * Policy to apply when CRL data is unavailable. */ private final RevocationPolicy<Void> unavailableCRLPolicy; /** * Policy to apply when CRL data has expired. */ private final RevocationPolicy<X509CRL> expiredCRLPolicy; /** * Instantiates a new Abstract crl revocation checker. * * @param checkAll Indicates whether all resources should be checked, * or revocation should stop at the first resource * that produces the cert. * @param unavailableCRLPolicy the unavailable crl policy * @param expiredCRLPolicy the expired crl policy */ public AbstractCRLRevocationChecker(final boolean checkAll, final RevocationPolicy<Void> unavailableCRLPolicy, final RevocationPolicy<X509CRL> expiredCRLPolicy) { this.checkAll = checkAll; this.unavailableCRLPolicy = unavailableCRLPolicy == null ? new DenyRevocationPolicy() : unavailableCRLPolicy; this.expiredCRLPolicy = expiredCRLPolicy == null ? new ThresholdExpiredCRLRevocationPolicy(0) : expiredCRLPolicy; } @Override public void check(final X509Certificate cert) throws GeneralSecurityException { if (cert == null) { throw new IllegalArgumentException("Certificate cannot be null."); } LOGGER.debug("Evaluating certificate revocation status for [{}]", CertUtils.toString(cert)); final Collection<X509CRL> crls = getCRLs(cert); if (crls == null || crls.isEmpty()) { LOGGER.warn("CRL data is not available for [{}]", CertUtils.toString(cert)); this.unavailableCRLPolicy.apply(null); return; } final List<X509CRL> expiredCrls = new ArrayList<>(); final List<X509CRLEntry> revokedCrls; crls.stream().filter(CertUtils::isExpired).forEach(crl -> { LOGGER.warn("CRL data expired on [{}]", crl.getNextUpdate()); expiredCrls.add(crl); }); if (crls.size() == expiredCrls.size()) { LOGGER.warn("All CRLs retrieved have expired. Applying CRL expiration policy..."); for (final X509CRL crl : expiredCrls) { this.expiredCRLPolicy.apply(crl); } } else { crls.removeAll(expiredCrls); LOGGER.debug("Valid CRLs [{}] found that are not expired yet", crls); revokedCrls = crls.stream().map(crl -> crl.getRevokedCertificate(cert)).filter(Objects::nonNull).collect(Collectors.toList()); if (revokedCrls.size() == crls.size()) { final X509CRLEntry entry = revokedCrls.get(0); LOGGER.warn("All CRL entries have been revoked. Rejecting the first entry [{}]", entry); throw new RevokedCertificateException(entry); } } } public RevocationPolicy<Void> getUnavailableCRLPolicy() { return this.unavailableCRLPolicy; } public RevocationPolicy<X509CRL> getExpiredCRLPolicy() { return this.expiredCRLPolicy; } /** * Records the addition of a new CRL entry. * * @param id the id of the entry to keep track of * @param crl new CRL entry * @return true if the entry was added successfully. * @since 4.1 */ protected abstract boolean addCRL(Object id, X509CRL crl); /** * Gets the collection of CRLs for the given certificate. * * @param cert Certificate for which the CRL of the issuing CA should be retrieved. * @return CRLs for given cert. */ protected abstract Collection<X509CRL> getCRLs(X509Certificate cert); }