2424
2525import eu .webeid .ocsp .OcspCertificateRevocationChecker ;
2626import eu .webeid .ocsp .client .OcspClient ;
27- import eu .webeid .ocsp .exceptions .UserCertificateOCSPCheckFailedException ;
27+ import eu .webeid .ocsp .exceptions .OCSPClientException ;
2828import eu .webeid .ocsp .exceptions .UserCertificateRevokedException ;
29- import eu .webeid .ocsp .exceptions .UserCertificateUnknownException ;
3029import eu .webeid .ocsp .protocol .OcspRequestBuilder ;
3130import eu .webeid .ocsp .service .OcspService ;
3231import eu .webeid .ocsp .service .OcspServiceProvider ;
32+ import eu .webeid .resilientocsp .exceptions .ResilientUserCertificateOCSPCheckFailedException ;
33+ import eu .webeid .resilientocsp .exceptions .ResilientUserCertificateRevokedException ;
3334import eu .webeid .security .exceptions .AuthTokenException ;
35+ import eu .webeid .security .validator .ValidationInfo ;
3436import eu .webeid .security .validator .revocationcheck .RevocationInfo ;
3537import io .github .resilience4j .circuitbreaker .CallNotPermittedException ;
3638import io .github .resilience4j .circuitbreaker .CircuitBreaker ;
4547import org .bouncycastle .asn1 .ocsp .OCSPResponseStatus ;
4648import org .bouncycastle .cert .ocsp .BasicOCSPResp ;
4749import org .bouncycastle .cert .ocsp .CertificateID ;
48- import org .bouncycastle .cert .ocsp .OCSPException ;
4950import org .bouncycastle .cert .ocsp .OCSPReq ;
5051import org .bouncycastle .cert .ocsp .OCSPResp ;
51- import org .bouncycastle .operator .OperatorCreationException ;
5252import org .slf4j .Logger ;
5353import org .slf4j .LoggerFactory ;
5454
55- import java .io .IOException ;
5655import java .net .URI ;
5756import java .security .cert .CertificateException ;
5857import java .security .cert .X509Certificate ;
5958import java .time .Duration ;
59+ import java .util .ArrayList ;
60+ import java .util .HashMap ;
6061import java .util .List ;
6162import java .util .Map ;
6263
@@ -104,14 +105,27 @@ public List<RevocationInfo> validateCertificateNotRevoked(X509Certificate subjec
104105 try {
105106 ocspService = getOcspServiceProvider ().getService (subjectCertificate );
106107 } catch (CertificateException e ) {
107- throw new UserCertificateOCSPCheckFailedException ( e , null );
108+ throw new ResilientUserCertificateOCSPCheckFailedException ( new ValidationInfo ( subjectCertificate , List . of ()) );
108109 }
109110 final OcspService fallbackOcspService = ocspService .getFallbackService ();
110111 if (fallbackOcspService == null ) {
111112 return List .of (request (ocspService , subjectCertificate , issuerCertificate , false ));
112113 }
113114
114115 CircuitBreaker circuitBreaker = circuitBreakerRegistry .circuitBreaker (ocspService .getAccessLocation ().toASCIIString ());
116+
117+ List <RevocationInfo > revocationInfoList = new ArrayList <>();
118+ circuitBreaker .getEventPublisher ().onError (event -> {
119+ Throwable throwable = event .getThrowable ();
120+ if (throwable instanceof ResilientUserCertificateOCSPCheckFailedException e ) {
121+ revocationInfoList .addAll (e .getValidationInfo ().revocationInfoList ());
122+ return ;
123+ }
124+ revocationInfoList .add (new RevocationInfo (null , Map .ofEntries (
125+ Map .entry (RevocationInfo .KEY_OCSP_ERROR , throwable )
126+ )));
127+ });
128+
115129 CheckedFunction0 <RevocationInfo > primarySupplier = () -> request (ocspService , subjectCertificate , issuerCertificate , false );
116130 CheckedFunction0 <RevocationInfo > fallbackSupplier = () -> request (ocspService .getFallbackService (), subjectCertificate , issuerCertificate , true );
117131 Decorators .DecorateCheckedSupplier <RevocationInfo > decorateCheckedSupplier = Decorators .ofCheckedSupplier (primarySupplier );
@@ -120,26 +134,40 @@ public List<RevocationInfo> validateCertificateNotRevoked(X509Certificate subjec
120134 decorateCheckedSupplier .withRetry (retry );
121135 }
122136 decorateCheckedSupplier .withCircuitBreaker (circuitBreaker )
123- .withFallback (List .of (UserCertificateOCSPCheckFailedException .class , CallNotPermittedException . class , UserCertificateUnknownException .class ), e -> fallbackSupplier .apply ());
137+ .withFallback (List .of (ResilientUserCertificateOCSPCheckFailedException .class , CallNotPermittedException .class ), e -> fallbackSupplier .apply ());
124138
125139 CheckedFunction0 <RevocationInfo > decoratedSupplier = decorateCheckedSupplier .decorate ();
126140
127- // TODO Collect the intermediate results
128- return List .of (Try .of (decoratedSupplier ).getOrElseThrow (throwable -> {
129- if (throwable instanceof AuthTokenException ) {
130- return (AuthTokenException ) throwable ;
141+ Try <RevocationInfo > result = Try .of (decoratedSupplier );
142+
143+ RevocationInfo revocationInfo = result .getOrElseThrow (throwable -> {
144+ if (throwable instanceof ResilientUserCertificateOCSPCheckFailedException exception ) {
145+ revocationInfoList .addAll (exception .getValidationInfo ().revocationInfoList ());
146+ exception .setValidationInfo (new ValidationInfo (subjectCertificate , revocationInfoList ));
147+ return exception ;
148+ }
149+ if (throwable instanceof ResilientUserCertificateRevokedException exception ) {
150+ revocationInfoList .addAll (exception .getValidationInfo ().revocationInfoList ());
151+ exception .setValidationInfo (new ValidationInfo (subjectCertificate , revocationInfoList ));
152+ return exception ;
131153 }
132- return new UserCertificateOCSPCheckFailedException (throwable , null );
133- }));
154+ // TODO This should always be TaraUserCertificateOCSPCheckFailedException when reached?
155+ return new ResilientUserCertificateOCSPCheckFailedException (new ValidationInfo (subjectCertificate , revocationInfoList ));
156+ });
157+
158+ revocationInfoList .add (revocationInfo );
159+ return revocationInfoList ;
134160 }
135161
136- private RevocationInfo request (OcspService ocspService , X509Certificate subjectCertificate , X509Certificate issuerCertificate , boolean allowThisUpdateInPast ) throws AuthTokenException {
162+ private RevocationInfo request (OcspService ocspService , X509Certificate subjectCertificate , X509Certificate issuerCertificate , boolean allowThisUpdateInPast ) throws ResilientUserCertificateOCSPCheckFailedException , ResilientUserCertificateRevokedException {
137163 URI ocspResponderUri = null ;
164+ OCSPResp response = null ;
165+ OCSPReq request = null ;
138166 try {
139167 ocspResponderUri = requireNonNull (ocspService .getAccessLocation (), "ocspResponderUri" );
140168
141169 final CertificateID certificateId = getCertificateId (subjectCertificate , issuerCertificate );
142- final OCSPReq request = new OcspRequestBuilder ()
170+ request = new OcspRequestBuilder ()
143171 .withCertificateId (certificateId )
144172 .enableOcspNonce (ocspService .doesSupportNonce ())
145173 .build ();
@@ -149,14 +177,28 @@ private RevocationInfo request(OcspService ocspService, X509Certificate subjectC
149177 }
150178
151179 LOG .debug ("Sending OCSP request" );
152- OCSPResp response = requireNonNull (getOcspClient ().request (ocspResponderUri , request )); // TODO: This should trigger fallback?
180+ response = requireNonNull (getOcspClient ().request (ocspResponderUri , request )); // TODO: This should trigger fallback?
153181 if (response .getStatus () != OCSPResponseStatus .SUCCESSFUL ) {
154- throw new UserCertificateOCSPCheckFailedException ("Response status: " + ocspStatusToString (response .getStatus ()), ocspResponderUri );
182+ ResilientUserCertificateOCSPCheckFailedException exception = new ResilientUserCertificateOCSPCheckFailedException ("Response status: " + ocspStatusToString (response .getStatus ()));
183+ RevocationInfo revocationInfo = new RevocationInfo (ocspService .getAccessLocation (), Map .ofEntries (
184+ Map .entry (RevocationInfo .KEY_OCSP_ERROR , exception ),
185+ Map .entry (RevocationInfo .KEY_OCSP_REQUEST , request ),
186+ Map .entry (RevocationInfo .KEY_OCSP_RESPONSE , response )
187+ ));
188+ exception .setValidationInfo (new ValidationInfo (subjectCertificate , List .of (revocationInfo )));
189+ throw exception ;
155190 }
156191
157192 final BasicOCSPResp basicResponse = (BasicOCSPResp ) response .getResponseObject ();
158193 if (basicResponse == null ) {
159- throw new UserCertificateOCSPCheckFailedException ("Missing Basic OCSP Response" , ocspResponderUri );
194+ ResilientUserCertificateOCSPCheckFailedException exception = new ResilientUserCertificateOCSPCheckFailedException ("Missing Basic OCSP Response" );
195+ RevocationInfo revocationInfo = new RevocationInfo (ocspService .getAccessLocation (), Map .ofEntries (
196+ Map .entry (RevocationInfo .KEY_OCSP_ERROR , exception ),
197+ Map .entry (RevocationInfo .KEY_OCSP_REQUEST , request ),
198+ Map .entry (RevocationInfo .KEY_OCSP_RESPONSE , response )
199+ ));
200+ exception .setValidationInfo (new ValidationInfo (subjectCertificate , List .of (revocationInfo )));
201+ throw exception ;
160202 }
161203 LOG .debug ("OCSP response received successfully" );
162204
@@ -170,24 +212,44 @@ private RevocationInfo request(OcspService ocspService, X509Certificate subjectC
170212 Map .entry (RevocationInfo .KEY_OCSP_REQUEST , request ),
171213 Map .entry (RevocationInfo .KEY_OCSP_RESPONSE , response )
172214 ));
173- } catch (OCSPException | CertificateException | OperatorCreationException | IOException e ) {
174- throw new UserCertificateOCSPCheckFailedException (e , ocspResponderUri );
215+ } catch (UserCertificateRevokedException e ) {
216+ RevocationInfo revocationInfo = getRevocationInfo (ocspResponderUri , e , request , response );
217+ throw new ResilientUserCertificateRevokedException (new ValidationInfo (subjectCertificate , List .of (revocationInfo )));
218+ } catch (OCSPClientException e ) {
219+ RevocationInfo revocationInfo = getRevocationInfo (ocspResponderUri , e , request , response );
220+ revocationInfo .ocspResponseAttributes ().put (RevocationInfo .KEY_OCSP_RESPONSE , e .getResponseBody ());
221+ revocationInfo .ocspResponseAttributes ().put (RevocationInfo .KEY_HTTP_STATUS_CODE , e .getStatusCode ());
222+ throw new ResilientUserCertificateOCSPCheckFailedException (new ValidationInfo (subjectCertificate , List .of (revocationInfo )));
223+ } catch (Exception e ) {
224+ RevocationInfo revocationInfo = getRevocationInfo (ocspResponderUri , e , request , response );
225+ throw new ResilientUserCertificateOCSPCheckFailedException (new ValidationInfo (subjectCertificate , List .of (revocationInfo )));
226+ }
227+ }
228+
229+ private RevocationInfo getRevocationInfo (URI ocspResponderUri , Exception e , OCSPReq request , OCSPResp response ) {
230+ RevocationInfo revocationInfo = new RevocationInfo (ocspResponderUri , new HashMap <>(Map .of (RevocationInfo .KEY_OCSP_ERROR , e )));
231+ if (request != null ) {
232+ revocationInfo .ocspResponseAttributes ().put (RevocationInfo .KEY_OCSP_REQUEST , request );
233+ }
234+ if (response != null ) {
235+ revocationInfo .ocspResponseAttributes ().put (RevocationInfo .KEY_OCSP_RESPONSE , response );
175236 }
237+ return revocationInfo ;
176238 }
177239
178240 private static CircuitBreakerConfig getCircuitBreakerConfig (CircuitBreakerConfig circuitBreakerConfig ) {
179241 return CircuitBreakerConfig .from (circuitBreakerConfig )
180242 // Users must not be able to modify these three values.
181243 .slidingWindowType (CircuitBreakerConfig .SlidingWindowType .COUNT_BASED )
182- .ignoreExceptions (UserCertificateRevokedException .class )
244+ .ignoreExceptions (ResilientUserCertificateRevokedException .class )
183245 .automaticTransitionFromOpenToHalfOpenEnabled (true )
184246 .build ();
185247 }
186248
187249 private static RetryConfig getRetryConfigConfig (RetryConfig retryConfig ) {
188250 return RetryConfig .from (retryConfig )
189251 // Users must not be able to modify this value.
190- .ignoreExceptions (UserCertificateRevokedException .class )
252+ .ignoreExceptions (ResilientUserCertificateRevokedException .class )
191253 .build ();
192254 }
193255}
0 commit comments