@@ -116,7 +116,34 @@ public List<RevocationInfo> validateCertificateNotRevoked(X509Certificate subjec
116116 List <RevocationInfo > revocationInfoList = new ArrayList <>();
117117
118118 CheckedFunction0 <RevocationInfo > primarySupplier = () -> request (ocspService , subjectCertificate , issuerCertificate , false );
119- CheckedFunction0 <RevocationInfo > fallbackSupplier = () -> request (ocspService .getFallbackService (), subjectCertificate , issuerCertificate , true );
119+ OcspService firstFallbackService = ocspService .getFallbackService ();
120+ CheckedFunction0 <RevocationInfo > firstFallbackSupplier = () -> request (firstFallbackService , subjectCertificate , issuerCertificate , true );
121+ OcspService secondFallbackService = getOcspServiceProvider ().getFallbackService (firstFallbackService .getAccessLocation ());
122+ CheckedFunction0 <RevocationInfo > fallbackSupplier ;
123+ if (secondFallbackService == null ) {
124+ fallbackSupplier = firstFallbackSupplier ;
125+ } else {
126+ CheckedFunction0 <RevocationInfo > secondFallbackSupplier = () -> request (secondFallbackService , subjectCertificate , issuerCertificate , true );
127+ fallbackSupplier = () -> {
128+ try {
129+ return firstFallbackSupplier .apply ();
130+ } catch (ResilientUserCertificateRevokedException e ) {
131+ // NOTE: ResilientUserCertificateRevokedException must be re-thrown before the generic
132+ // catch (Exception) block. Without this, a "revoked" verdict from the first fallback would
133+ // be swallowed, and the second fallback could silently override it with a "good" response.
134+ throw e ;
135+ } catch (Exception e ) {
136+ if (e instanceof ResilientUserCertificateOCSPCheckFailedException exception ) {
137+ revocationInfoList .addAll ((exception .getValidationInfo ().revocationInfoList ()));
138+ } else {
139+ revocationInfoList .add (new RevocationInfo (null , Map .ofEntries (
140+ Map .entry (RevocationInfo .KEY_OCSP_ERROR , e )
141+ )));
142+ }
143+ return secondFallbackSupplier .apply ();
144+ }
145+ };
146+ }
120147 Decorators .DecorateCheckedSupplier <RevocationInfo > decorateCheckedSupplier = Decorators .ofCheckedSupplier (primarySupplier );
121148 if (retryRegistry != null ) {
122149 Retry retry = retryRegistry .retry (ocspService .getAccessLocation ().toASCIIString ());
0 commit comments