@@ -733,28 +733,56 @@ public TrustAnchor findTrustAnchor(PKIXParameters params,
733733 return anchorFound ;
734734 }
735735
736+ /**
737+ * Throw CertPathValidatorException for undetermined revocation status.
738+ *
739+ * Used when revocation checking is enabled but no CRLs are available
740+ * and no PKIXRevocationChecker is configured to handle OCSP.
741+ *
742+ * @param message error message describing the revocation failure
743+ * @param certPath the CertPath being validated (for exception reporting)
744+ * @param certs list of certificates from certPath
745+ *
746+ * @throws CertPathValidatorException always thrown with
747+ * UNDETERMINED_REVOCATION_STATUS reason
748+ */
749+ private void throwUndeterminedRevocationStatus (String message ,
750+ CertPath certPath , List <X509Certificate > certs )
751+ throws CertPathValidatorException {
752+
753+ /* Report index of last cert in path (closest to trust anchor)
754+ * to match SunJCE behavior. */
755+ int failIndex = 0 ;
756+ if (certs != null && certs .size () > 1 ) {
757+ failIndex = certs .size () - 1 ;
758+ }
759+ throw new CertPathValidatorException (message , null , certPath ,
760+ failIndex , BasicReason .UNDETERMINED_REVOCATION_STATUS );
761+ }
762+
736763 /**
737764 * Check if revocation has been enabled in PKIXParameters, and if so
738765 * find and load any CRLs in params.getCertStores().
739766 *
740767 * When a PKIXRevocationChecker has been registered via
741768 * addCertPathChecker(), that checker handles revocation checking. CRL
742769 * checking in the native CertManager is only enabled if:
743- * - No PKIXRevocationChecker is present (default CRL behavior), OR
770+ * - No PKIXRevocationChecker is present (default CRL behavior), or
744771 * - PKIXRevocationChecker has PREFER_CRLS option set
745772 *
746773 * @param params parameters used to check if revocation is enabled
747- * and if so load any CRLs available
774+ * and, if so load any CRLs available
748775 * @param cm WolfSSLCertManager to load CRLs into
749- * @param targetCert peer/leaf cert used to find matching CRL
776+ * @param certPath the CertPath being validated (for exception reporting)
777+ * @param certs list of certificates from certPath
750778 * @param pathCheckers list of registered CertPathCheckers
751779 *
752780 * @throws CertPathValidatorException if error is encountered during
753781 * revocation checking or CRL loading
754782 */
755783 private void checkRevocationEnabledAndLoadCRLs (
756784 PKIXParameters params , WolfSSLCertManager cm ,
757- X509Certificate targetCert ,
785+ CertPath certPath , List < X509Certificate > certs ,
758786 List <PKIXCertPathChecker > pathCheckers )
759787 throws CertPathValidatorException {
760788
@@ -810,12 +838,22 @@ private void checkRevocationEnabledAndLoadCRLs(
810838
811839 /* Enable CRL in native WolfSSLCertManager */
812840 cm .CertManagerEnableCRL (WolfCrypt .WOLFSSL_CRL_CHECK );
813-
814841 log ("CRL support enabled in native WolfSSLCertManager" );
815842
816843 stores = params .getCertStores ();
817844 if (stores == null || stores .isEmpty ()) {
818845 log ("no CertStores in PKIXParameters to load CRLs" );
846+
847+ /* If revocation is enabled but no CRLs and no
848+ * PKIXRevocationChecker to handle OCSP, we cannot determine
849+ * revocation status. Per RFC 5280, this should fail. */
850+ if (!hasRevocationChecker ) {
851+ throwUndeterminedRevocationStatus (
852+ "Revocation checking enabled but no CRLs available " +
853+ "and no PKIXRevocationChecker configured for OCSP" ,
854+ certPath , certs );
855+ }
856+
819857 return ;
820858 }
821859
@@ -850,7 +888,7 @@ private void checkRevocationEnabledAndLoadCRLs(
850888
851889 /* Create CRL selector to help match target X509Certificate */
852890 X509CRLSelector selector = new X509CRLSelector ();
853- selector .setCertificateChecking (targetCert );
891+ selector .setCertificateChecking (certs . get ( 0 ) );
854892
855893 try {
856894 /* Find and load any matching CRLs */
@@ -868,6 +906,16 @@ private void checkRevocationEnabledAndLoadCRLs(
868906 }
869907
870908 log ("loaded " + loadedCount + " CRLs into WolfSSLCertManager" );
909+
910+ /* If no CRLs were loaded and no PKIXRevocationChecker is handling
911+ * OCSP, we cannot determine revocation status. */
912+ if (loadedCount == 0 && !hasRevocationChecker ) {
913+ throwUndeterminedRevocationStatus (
914+ "Revocation checking enabled but no CRLs found in " +
915+ "CertStores and no PKIXRevocationChecker configured " +
916+ "for OCSP" ,
917+ certPath , certs );
918+ }
871919 }
872920 else {
873921 log ("revocation not enabled in PKIXParameters" );
@@ -953,6 +1001,31 @@ public CertPathValidatorResult engineValidate(
9531001 }
9541002 }
9551003
1004+ /* Zero-length cert paths are valid per RFC 5280. This occurs when
1005+ * CertPathBuilder determines the trust anchor itself is the target
1006+ * (no intermediate certificates needed). Return success with the
1007+ * trust anchor's public key. No need to create CertManager. */
1008+ if (certPath .getCertificates ().isEmpty ()) {
1009+ Set <TrustAnchor > anchors = pkixParams .getTrustAnchors ();
1010+ if (anchors == null || anchors .isEmpty ()) {
1011+ throw new CertPathValidatorException (
1012+ "No TrustAnchors in PKIXParameters" );
1013+ }
1014+
1015+ /* Return first trust anchor for zero-length path */
1016+ TrustAnchor anchor = anchors .iterator ().next ();
1017+ X509Certificate anchorCert = anchor .getTrustedCert ();
1018+ if (anchorCert == null ) {
1019+ throw new CertPathValidatorException (
1020+ "TrustAnchor has no certificate for zero-length path" );
1021+ }
1022+ log ("Zero-length cert path, returning trust anchor: " +
1023+ anchorCert .getSubjectX500Principal ().getName ());
1024+
1025+ return new PKIXCertPathValidatorResult (anchor , null ,
1026+ anchorCert .getPublicKey ());
1027+ }
1028+
9561029 /* Use wolfSSL CertManager to do chain verification */
9571030 try {
9581031 cm = new WolfSSLCertManager ();
@@ -972,10 +1045,6 @@ public CertPathValidatorResult engineValidate(
9721045 certs .add ((X509Certificate ) cert );
9731046 }
9741047 }
975- if (certs .size () == 0 ) {
976- throw new CertPathValidatorException (
977- "No Certificate objects in CertPath" );
978- }
9791048
9801049 /* Register verify callback to override date validation if
9811050 * PKIXParameters specifies an override date */
@@ -1019,7 +1088,7 @@ public CertPathValidatorResult engineValidate(
10191088 * will try to find/verify CRL against trusted roots on load.
10201089 * Pass pathCheckers so we can skip CRL setup when a
10211090 * PKIXRevocationChecker is handling revocation via OCSP. */
1022- checkRevocationEnabledAndLoadCRLs (pkixParams , cm , certs . get ( 0 ) ,
1091+ checkRevocationEnabledAndLoadCRLs (pkixParams , cm , certPath , certs ,
10231092 pathCheckers );
10241093
10251094 /* Verify cert chain */
0 commit comments