F-11212: fail closed when PREFER_CRLS with NO_FALLBACK has no CRL source

pull/265/head
Chris Conlon 2026-08-24 15:35:29 -06:00
parent ec8d69466c
commit a3da1774cc
4 changed files with 535 additions and 33 deletions

View File

@ -888,6 +888,10 @@ revoked, validation will fail. The difference only affects behavior when one
method succeeds and the other would have failed (e.g., OCSP unreachable but
CRL available).
A `PKIXRevocationChecker` added with `addCertPathChecker()` applies
irregardless of if `setRevocationEnabled()` is set, so `PREFER_CRLS` with CRLs
in the `CertStore` list performs CRL checking even when revocation is disabled.
#### Indirect CRL Not Supported
Native wolfSSL does not support indirect CRLs. An indirect CRL is a CRL signed

View File

@ -73,7 +73,8 @@ import java.security.cert.CertificateException;
* validation will not return PolicyNode in CertPathValidatorResult
*
* Revocation checking is supported via:
* - CRL: If PKIXParameters.isRevocationEnabled() is true and appropriate
* - CRL: If PKIXParameters.isRevocationEnabled() is true, or a
* PKIXRevocationChecker with PREFER_CRLS is registered, and appropriate
* CRLs have been loaded into CertStore Set
* - OCSP: via getRevocationChecker() which returns a
* WolfCryptPKIXRevocationChecker supporting OCSP and options
@ -839,25 +840,71 @@ public class WolfCryptPKIXCertPathValidator extends CertPathValidatorSpi {
CertPath certPath, List<X509Certificate> certs)
throws CertPathValidatorException {
/* Report index of last cert in path (closest to trust anchor)
* to match SunJCE behavior. */
int failIndex = 0;
if (certs != null && certs.size() > 1) {
failIndex = certs.size() - 1;
}
throw new CertPathValidatorException(message, null, certPath,
failIndex, BasicReason.UNDETERMINED_REVOCATION_STATUS);
lastCertIndex(certs), BasicReason.UNDETERMINED_REVOCATION_STATUS);
}
/**
* Check if revocation has been enabled in PKIXParameters, and if so
* find and load any CRLs in params.getCertStores().
* Index of cert closest to the trust anchor.
*
* When a PKIXRevocationChecker is registered via addCertPathChecker(),
* that checker handles revocation checking. CRL checking in the native
* CertManager is only enabled if:
* - No PKIXRevocationChecker is present (default CRL behavior), or
* - PKIXRevocationChecker has PREFER_CRLS option set
* @param certs certificate list from the CertPath
*
* @return index of the last cert, or 0 for a single-cert path
*/
private static int lastCertIndex(List<X509Certificate> certs) {
if (certs != null && certs.size() > 1) {
return certs.size() - 1;
}
return 0;
}
/**
* Disable native CRL checking after a PREFER_CRLS checker found no CRL
* to load, so wolfSSL doesn't fail chain validation on a missing CRL. With
* NO_FALLBACK set, revocation is undetermined, which fails validation
* unless SOFT_FAIL is set. Without NO_FALLBACK, the OCSP result the
* checker already produced in check() propogates.
*
* @param revChecker the registered PREFER_CRLS checker
* @param noFallback true if the checker has NO_FALLBACK set
* @param cm WolfSSLCertManager with CRL checking enabled
* @param certPath the CertPath being validated, for exception reporting
* @param failIndex index of the cert to report
*
* @throws CertPathValidatorException if revocation is undetermined and
* SOFT_FAIL is not set, or native CRL checking cannot be disabled
*/
private void handleMissingCrl(WolfCryptPKIXRevocationChecker revChecker,
boolean noFallback, WolfSSLCertManager cm, CertPath certPath,
int failIndex) throws CertPathValidatorException {
if (noFallback) {
revChecker.handleMissingCrlRevocation(certPath, failIndex);
}
else {
log("no CRL loaded, PREFER_CRLS checker falls back to OCSP");
}
try {
cm.CertManagerDisableCRL();
}
catch (WolfCryptException e) {
throw new CertPathValidatorException("Failed to disable CRL " +
"checking in native WolfSSLCertManager", e);
}
}
/**
* Check if CRL checking is wanted and, if so, find and load any CRLs in
* params.getCertStores().
*
* CRL checking in the native CertManager is enabled when
* PKIXParameters.isRevocationEnabled() is true, or when a
* PKIXRevocationChecker with PREFER_CRLS is registered, which applies
* irregardless of the revocation flag. A registered checker without
* PREFER_CRLS handles revocation itself via OCSP.
*
* @param params parameters used to check if revocation is enabled and,
* if so load any CRLs available
@ -877,10 +924,13 @@ public class WolfCryptPKIXCertPathValidator extends CertPathValidatorSpi {
int i = 0;
int loadedCount = 0;
int certCount = 0;
int failIndex = lastCertIndex(certs);
List<CertStore> stores = null;
Collection<? extends CRL> crls = null;
boolean hasRevocationChecker = false;
boolean preferCrls = false;
boolean noFallback = false;
WolfCryptPKIXRevocationChecker revChecker = null;
if (params == null || cm == null) {
throw new CertPathValidatorException(
@ -894,13 +944,14 @@ public class WolfCryptPKIXCertPathValidator extends CertPathValidatorSpi {
for (PKIXCertPathChecker checker : pathCheckers) {
if (checker instanceof WolfCryptPKIXRevocationChecker) {
hasRevocationChecker = true;
WolfCryptPKIXRevocationChecker revChecker =
(WolfCryptPKIXRevocationChecker)checker;
revChecker = (WolfCryptPKIXRevocationChecker)checker;
Set<PKIXRevocationChecker.Option> options =
revChecker.getOptions();
if (options != null && options.contains(
PKIXRevocationChecker.Option.PREFER_CRLS)) {
preferCrls = true;
if (options != null) {
preferCrls = options.contains(
PKIXRevocationChecker.Option.PREFER_CRLS);
noFallback = options.contains(
PKIXRevocationChecker.Option.NO_FALLBACK);
}
break;
}
@ -913,18 +964,24 @@ public class WolfCryptPKIXCertPathValidator extends CertPathValidatorSpi {
return;
}
if (params.isRevocationEnabled()) {
log("revocation enabled in PKIXParameters, checking for CRLs " +
"to load");
if (params.isRevocationEnabled() || preferCrls) {
log("revocation enabled or PREFER_CRLS checker registered, " +
"checking for CRLs to load");
if (!WolfCrypt.CrlEnabled()) {
throw new CertPathValidatorException(
"Revocation enabled in PKIXParameters but native " +
"wolfCrypt CRL not compiled in");
"CRL checking requested but native wolfCrypt CRL not " +
"compiled in");
}
/* Enable CRL in native WolfSSLCertManager */
cm.CertManagerEnableCRL(WolfCrypt.WOLFSSL_CRL_CHECK);
try {
cm.CertManagerEnableCRL(WolfCrypt.WOLFSSL_CRL_CHECK);
}
catch (WolfCryptException e) {
throw new CertPathValidatorException("Failed to enable CRL " +
"checking in native WolfSSLCertManager", e);
}
log("CRL support enabled in native WolfSSLCertManager");
stores = params.getCertStores();
@ -940,6 +997,10 @@ public class WolfCryptPKIXCertPathValidator extends CertPathValidatorSpi {
"and no PKIXRevocationChecker configured for OCSP",
certPath, certs);
}
else {
handleMissingCrl(revChecker, noFallback, cm, certPath,
failIndex);
}
return;
}
@ -990,22 +1051,32 @@ public class WolfCryptPKIXCertPathValidator extends CertPathValidatorSpi {
}
} catch (CertStoreException e) {
throw new CertPathValidatorException(e);
} catch (WolfCryptException e) {
throw new CertPathValidatorException(
"Failed to load CRL into native WolfSSLCertManager", e);
}
log("loaded " + loadedCount + " CRLs into WolfSSLCertManager");
/* If no CRLs were loaded and no PKIXRevocationChecker is handling
* OCSP, we cannot determine revocation status. */
if (loadedCount == 0 && !hasRevocationChecker) {
throwUndeterminedRevocationStatus(
"Revocation checking enabled but no CRLs found in " +
"CertStores and no PKIXRevocationChecker configured " +
"for OCSP",
certPath, certs);
if (loadedCount == 0) {
if (!hasRevocationChecker) {
throwUndeterminedRevocationStatus(
"Revocation checking enabled but no CRLs found in " +
"CertStores and no PKIXRevocationChecker configured " +
"for OCSP",
certPath, certs);
}
else {
handleMissingCrl(revChecker, noFallback, cm, certPath,
failIndex);
}
}
}
else {
log("revocation not enabled in PKIXParameters");
log("revocation not enabled in PKIXParameters and no PREFER_CRLS" +
"checker registered");
}
}

View File

@ -24,6 +24,7 @@ package com.wolfssl.provider.jce;
import java.net.URI;
import java.security.cert.Certificate;
import java.security.cert.CertificateEncodingException;
import java.security.cert.CertPath;
import java.security.cert.CertPathValidatorException;
import java.security.cert.CertPathValidatorException.BasicReason;
import java.security.cert.Extension;
@ -543,6 +544,27 @@ public class WolfCryptPKIXRevocationChecker extends PKIXRevocationChecker {
}
}
/**
* Fail closed when a PREFER_CRLS/NO_FALLBACK checker has no CRL source.
*
* check() runs before CRLs are loaded and cannot see whether a CRL source
* is configured, so the validator calls this after CRL setup. With OCSP
* suppressed by NO_FALLBACK and no CRL to check, revocation status is
* undetermined. Honors SOFT_FAIL via handleException().
*
* @param certPath the CertPath being validated, for exception reporting
* @param index index of the cert whose revocation status is undetermined
*
* @throws CertPathValidatorException if SOFT_FAIL is not set
*/
void handleMissingCrlRevocation(CertPath certPath, int index)
throws CertPathValidatorException {
handleException(new CertPathValidatorException(
"PREFER_CRLS with NO_FALLBACK selected but no CRL source is " +
"available, revocation status cannot be determined", null,
certPath, index, BasicReason.UNDETERMINED_REVOCATION_STATUS));
}
/**
* Set OCSP responder URI override.
*

View File

@ -33,6 +33,7 @@ import org.junit.BeforeClass;
import java.util.List;
import java.util.ArrayList;
import java.util.EnumSet;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
@ -41,6 +42,10 @@ import java.io.File;
import java.io.InputStream;
import java.io.IOException;
import java.io.FileInputStream;
import java.util.Arrays;
import java.nio.file.Paths;
import java.nio.file.Files;
import java.nio.charset.StandardCharsets;
import java.io.FileNotFoundException;
import java.security.Security;
import java.security.Provider;
@ -57,6 +62,7 @@ import java.security.cert.CertPath;
import java.security.cert.CertPathValidator;
import java.security.cert.CertPathValidatorResult;
import java.security.cert.PKIXParameters;
import java.security.cert.PKIXRevocationChecker;
import java.security.cert.PKIXCertPathChecker;
import java.security.cert.PKIXCertPathValidatorResult;
import java.security.cert.CertificateException;
@ -97,6 +103,7 @@ public class WolfCryptPKIXCertPathValidatorTest {
protected static String caCertDer = null; /* ca-cert.der */
protected static String caEccCertDer = null; /* ca-ecc-cert.der */
protected static String crlDer = null; /* crl.der */
protected static String crlRevoked = null; /* crl.revoked */
/* RSA-based cert chain with intermediates:
* server/peer: server-int-cert.pem/der
@ -208,6 +215,8 @@ public class WolfCryptPKIXCertPathValidatorTest {
crlDer =
certPre.concat("examples/certs/crl/crl.der");
crlRevoked =
certPre.concat("examples/certs/crl/crl.revoked");
}
/**
@ -314,6 +323,312 @@ public class WolfCryptPKIXCertPathValidatorTest {
checkPKIXCertPathValidatorResult(result, caCert, certPubKey);
}
/* Build a single server-cert path validated against the RSA CA anchor. */
private CertPath singleServerCertPath(CertificateFactory certFactory)
throws Exception {
List<Certificate> certList = new ArrayList<>();
InputStream fis = new FileInputStream(serverCertDer);
certList.add(certFactory.generateCertificate(fis));
fis.close();
return certFactory.generateCertPath(certList);
}
/**
* PREFER_CRLS with NO_FALLBACK and no CRL source must fail closed with
* UNDETERMINED_REVOCATION_STATUS.
*/
@Test
public void testPreferCrlsNoFallbackWithoutCrlFailsClosed()
throws Exception {
KeyStore store = createKeyStoreFromFile(jksCaServerRSA2048,
keyStorePass);
if (store == null || store.size() != 1) {
throw new Exception("Error creating KeyStore");
}
CertificateFactory certFactory =
CertificateFactory.getInstance("X.509");
CertPath path = singleServerCertPath(certFactory);
CertPathValidator cpv = CertPathValidator.getInstance("PKIX", provider);
PKIXRevocationChecker rc =
(PKIXRevocationChecker) cpv.getRevocationChecker();
rc.setOptions(EnumSet.of(PKIXRevocationChecker.Option.PREFER_CRLS,
PKIXRevocationChecker.Option.NO_FALLBACK));
PKIXParameters params = new PKIXParameters(store);
params.setRevocationEnabled(false);
params.addCertPathChecker(rc);
try {
cpv.validate(path, params);
fail("Validation should fail closed with no CRL source");
} catch (CertPathValidatorException e) {
assertEquals(BasicReason.UNDETERMINED_REVOCATION_STATUS,
e.getReason());
}
}
/**
* With SOFT_FAIL, the missing-CRL determination is soft, so validation
* completes instead of failing closed. The soft-fail exception is not
* asserted here because addCertPathChecker() clones rc, so it lands on
* the clone rather than rc.
*/
@Test
public void testPreferCrlsNoFallbackWithoutCrlSoftFailPasses()
throws Exception {
KeyStore store = createKeyStoreFromFile(jksCaServerRSA2048,
keyStorePass);
if (store == null || store.size() != 1) {
throw new Exception("Error creating KeyStore");
}
CertificateFactory certFactory =
CertificateFactory.getInstance("X.509");
CertPath path = singleServerCertPath(certFactory);
CertPathValidator cpv =
CertPathValidator.getInstance("PKIX", provider);
PKIXRevocationChecker rc =
(PKIXRevocationChecker) cpv.getRevocationChecker();
rc.setOptions(EnumSet.of(PKIXRevocationChecker.Option.PREFER_CRLS,
PKIXRevocationChecker.Option.NO_FALLBACK,
PKIXRevocationChecker.Option.SOFT_FAIL));
PKIXParameters params = new PKIXParameters(store);
params.setRevocationEnabled(false);
params.addCertPathChecker(rc);
cpv.validate(path, params);
}
/**
* SOFT_FAIL with revocation enabled but no CRL source must also complete.
* Revocation turns on the native CRL check, so the missing-CRL path must
* disable it under SOFT_FAIL to avoid a hard CRL_MISSING failure.
*/
@Test
public void testPreferCrlsNoFallbackRevocationEnabledSoftFailPasses()
throws Exception {
if (!WolfCrypt.CrlEnabled()) {
System.out.println("CertPathValidator revocation status test " +
"skipped, CRL not compiled in");
return;
}
KeyStore store = createKeyStoreFromFile(jksCaServerRSA2048,
keyStorePass);
if (store == null || store.size() != 1) {
throw new Exception("Error creating KeyStore");
}
CertificateFactory certFactory =
CertificateFactory.getInstance("X.509");
CertPath path = singleServerCertPath(certFactory);
CertPathValidator cpv = CertPathValidator.getInstance("PKIX", provider);
PKIXRevocationChecker rc =
(PKIXRevocationChecker) cpv.getRevocationChecker();
rc.setOptions(EnumSet.of(PKIXRevocationChecker.Option.PREFER_CRLS,
PKIXRevocationChecker.Option.NO_FALLBACK,
PKIXRevocationChecker.Option.SOFT_FAIL));
PKIXParameters params = new PKIXParameters(store);
params.setRevocationEnabled(true);
params.addCertPathChecker(rc);
cpv.validate(path, params);
}
/**
* Load a CRL from a file. Some CRL files carry a PEM text dump ahead
* of the PEM block, which not every CertificateFactory skips, so start
* at the PEM header when one is present.
*/
private CRL crlFromFile(CertificateFactory certFactory, String path)
throws Exception {
byte[] data = Files.readAllBytes(Paths.get(path));
int begin = new String(data, StandardCharsets.US_ASCII)
.indexOf("-----BEGIN X509 CRL-----");
if (begin > 0) {
data = Arrays.copyOfRange(data, begin, data.length);
}
return certFactory.generateCRL(new ByteArrayInputStream(data));
}
/**
* Validate the single server cert with a PREFER_CRLS/NO_FALLBACK checker
* and the given CRL supplied through a CertStore.
*/
private void validatePreferCrlsNoFallbackWithCrl(String crlPath,
boolean revocationEnabled) throws Exception {
KeyStore store = createKeyStoreFromFile(jksCaServerRSA2048,
keyStorePass);
if (store == null || store.size() != 1) {
throw new Exception("Error creating KeyStore");
}
CertificateFactory certFactory =
CertificateFactory.getInstance("X.509");
CertPath path = singleServerCertPath(certFactory);
CertPathValidator cpv = CertPathValidator.getInstance("PKIX", provider);
PKIXRevocationChecker rc =
(PKIXRevocationChecker) cpv.getRevocationChecker();
rc.setOptions(EnumSet.of(PKIXRevocationChecker.Option.PREFER_CRLS,
PKIXRevocationChecker.Option.NO_FALLBACK));
/* CRL files are issued by ca-cert.der, the root for server-cert */
Collection<CRL> crls = new HashSet<>();
crls.add(crlFromFile(certFactory, crlPath));
List<CertStore> certStores = new ArrayList<>();
certStores.add(CertStore.getInstance("Collection",
new CollectionCertStoreParameters(crls)));
PKIXParameters params = new PKIXParameters(store);
params.setCertStores(certStores);
params.setRevocationEnabled(revocationEnabled);
params.addCertPathChecker(rc);
cpv.validate(path, params);
}
/**
* Validate a server cert with a PREFER_CRLS checker that allows OCSP
* fallback, SOFT_FAIL set, and no CRL source.
*/
private void validatePreferCrlsFallbackSoftFailWithoutCrl(
boolean revocationEnabled) throws Exception {
KeyStore store = createKeyStoreFromFile(jksCaServerRSA2048,
keyStorePass);
if (store == null || store.size() != 1) {
throw new Exception("Error creating KeyStore");
}
CertificateFactory certFactory =
CertificateFactory.getInstance("X.509");
CertPath path = singleServerCertPath(certFactory);
CertPathValidator cpv = CertPathValidator.getInstance("PKIX", provider);
PKIXRevocationChecker rc =
(PKIXRevocationChecker) cpv.getRevocationChecker();
rc.setOptions(EnumSet.of(PKIXRevocationChecker.Option.PREFER_CRLS,
PKIXRevocationChecker.Option.SOFT_FAIL));
PKIXParameters params = new PKIXParameters(store);
params.setRevocationEnabled(revocationEnabled);
params.addCertPathChecker(rc);
cpv.validate(path, params);
}
/**
* PREFER_CRLS with fallback allowed and no CRL source must not fail on
* a missing CRL, revocation is then determined by OCSP alone. With
* SOFT_FAIL an unreachable OCSP responder is not fatal, so validation
* should complete successfully.
*/
@Test
public void testPreferCrlsFallbackWithoutCrlSoftFailPasses()
throws Exception {
if (!WolfCrypt.CrlEnabled()) {
System.out.println(
"PREFER_CRLS fallback test skipped, CRL not compiled in");
return;
}
validatePreferCrlsFallbackSoftFailWithoutCrl(false);
}
/**
* PREFER_CRLS with fallback allowed, SOFT_FAIL, no CRL source, and
* revocation enabled must not fail on the missing CRL, revocation is
* then determined only by OCSP.
*/
@Test
public void testPreferCrlsFallbackEnabledWithoutCrlSoftFailPasses()
throws Exception {
if (!WolfCrypt.CrlEnabled()) {
System.out.println(
"PREFER_CRLS fallback test skipped, CRL not compiled in");
return;
}
validatePreferCrlsFallbackSoftFailWithoutCrl(true);
}
/**
* A PREFER_CRLS/NO_FALLBACK checker with CRLs actually loaded must still
* validate a non-revoked cert. The missing-CRL fail-closed path must not
* fire when a CRL source is present.
*/
@Test
public void testPreferCrlsNoFallbackWithCrlValidates()
throws Exception {
if (!WolfCrypt.CrlEnabled()) {
System.out.println(
"PREFER_CRLS with CRL test skipped, CRL not compiled in");
return;
}
validatePreferCrlsNoFallbackWithCrl(crlDer, true);
}
/**
* A checker added with addCertPathChecker() applies irrespective of
* setRevocationEnabled(), so PREFER_CRLS with a CRL in the CertStores is
* a supported CRL-only setup even with revocation disabled.
*/
@Test
public void testPreferCrlsNoFallbackRevocationDisabledWithCrlValidates()
throws Exception {
if (!WolfCrypt.CrlEnabled()) {
System.out.println(
"PREFER_CRLS with CRL test skipped, CRL not compiled in");
return;
}
validatePreferCrlsNoFallbackWithCrl(crlDer, false);
}
/**
* PREFER_CRLS with NO_FALLBACK, revocation disabled, and a CRL in the
* CertStores that revokes the server cert must fail validation, proving
* the supplied CRL is checked and not just ignored.
*/
@Test
public void testPreferCrlsNoFallbackRevocationDisabledRevokedFails()
throws Exception {
if (!WolfCrypt.CrlEnabled()) {
System.out.println(
"PREFER_CRLS with CRL test skipped, CRL not compiled in");
return;
}
try {
validatePreferCrlsNoFallbackWithCrl(crlRevoked, false);
fail("Revoked cert should fail with revocation disabled and " +
"PREFER_CRLS");
} catch (CertPathValidatorException e) {
/* expected */
}
}
/**
* Test that setting the target cert constraints with
* PKIXParameters.setTargetCertConstraints() passes with correct cert
@ -2049,6 +2364,96 @@ public class WolfCryptPKIXCertPathValidatorTest {
}
}
/**
* PREFER_CRLS/NO_FALLBACK with revocation enabled and a CertStore that
* loads no matching CRL (loadedCount == 0) must fail closed.
*/
@Test
public void testPreferCrlsNoFallbackEmptyCertStoreFailsClosed()
throws Exception {
if (!WolfCrypt.CrlEnabled()) {
System.out.println("CertPathValidator revocation status test " +
"skipped, CRL not compiled in");
return;
}
KeyStore store = createKeyStoreFromFile(jksCaServerRSA2048,
keyStorePass);
if (store == null || store.size() != 1) {
throw new Exception("Error creating KeyStore");
}
CertificateFactory certFactory =
CertificateFactory.getInstance("X.509");
CertPath path = singleServerCertPath(certFactory);
CertPathValidator cpv = CertPathValidator.getInstance("PKIX", provider);
PKIXRevocationChecker rc =
(PKIXRevocationChecker) cpv.getRevocationChecker();
rc.setOptions(EnumSet.of(PKIXRevocationChecker.Option.PREFER_CRLS,
PKIXRevocationChecker.Option.NO_FALLBACK));
PKIXParameters params = new PKIXParameters(store);
params.setRevocationEnabled(true);
params.addCertPathChecker(rc);
/* Non-empty store list holding no matching CRL drives loadedCount 0 */
List<CertStore> certStores = new ArrayList<>();
certStores.add(CertStore.getInstance("Collection",
new CollectionCertStoreParameters(new HashSet<CRL>())));
params.setCertStores(certStores);
try {
cpv.validate(path, params);
fail("Expected UNDETERMINED_REVOCATION_STATUS");
} catch (CertPathValidatorException e) {
assertEquals(BasicReason.UNDETERMINED_REVOCATION_STATUS,
e.getReason());
}
}
/**
* Same as above with SOFT_FAIL, validation must complete because the
* missing-CRL path disables the native CRL check.
*/
@Test
public void testPreferCrlsNoFallbackEmptyCertStoreSoftFailPasses()
throws Exception {
if (!WolfCrypt.CrlEnabled()) {
System.out.println("CertPathValidator revocation status test " +
"skipped, CRL not compiled in");
return;
}
KeyStore store = createKeyStoreFromFile(jksCaServerRSA2048,
keyStorePass);
if (store == null || store.size() != 1) {
throw new Exception("Error creating KeyStore");
}
CertificateFactory certFactory =
CertificateFactory.getInstance("X.509");
CertPath path = singleServerCertPath(certFactory);
CertPathValidator cpv = CertPathValidator.getInstance("PKIX", provider);
PKIXRevocationChecker rc =
(PKIXRevocationChecker) cpv.getRevocationChecker();
rc.setOptions(EnumSet.of(PKIXRevocationChecker.Option.PREFER_CRLS,
PKIXRevocationChecker.Option.NO_FALLBACK,
PKIXRevocationChecker.Option.SOFT_FAIL));
PKIXParameters params = new PKIXParameters(store);
params.setRevocationEnabled(true);
params.addCertPathChecker(rc);
List<CertStore> certStores = new ArrayList<>();
certStores.add(CertStore.getInstance("Collection",
new CollectionCertStoreParameters(new HashSet<CRL>())));
params.setCertStores(certStores);
cpv.validate(path, params);
}
/**
* Test that zero-length cert paths are valid per RFC 5280. This occurs
* when CertPathBuilder determines the trust anchor itself is the target.