20 * |
20 * |
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
22 * or visit www.oracle.com if you need additional information or have any |
22 * or visit www.oracle.com if you need additional information or have any |
23 * questions. |
23 * questions. |
24 */ |
24 */ |
25 |
|
26 package sun.security.ssl; |
25 package sun.security.ssl; |
27 |
26 |
28 import java.io.IOException; |
27 import java.io.IOException; |
29 import java.net.URI; |
28 import java.net.URI; |
30 import java.net.URISyntaxException; |
29 import java.net.URISyntaxException; |
31 import java.security.AccessController; |
30 import java.security.AccessController; |
|
31 import java.security.cert.Extension; |
32 import java.security.cert.X509Certificate; |
32 import java.security.cert.X509Certificate; |
33 import java.security.cert.Extension; |
33 import java.util.ArrayList; |
34 import java.util.*; |
34 import java.util.Collections; |
35 import java.util.concurrent.*; |
35 import java.util.Date; |
36 |
36 import java.util.HashMap; |
|
37 import java.util.List; |
|
38 import java.util.Map; |
|
39 import java.util.Objects; |
|
40 import java.util.concurrent.Callable; |
|
41 import java.util.concurrent.ExecutionException; |
|
42 import java.util.concurrent.Executors; |
|
43 import java.util.concurrent.Future; |
|
44 import java.util.concurrent.ScheduledThreadPoolExecutor; |
|
45 import java.util.concurrent.ThreadFactory; |
|
46 import java.util.concurrent.ThreadPoolExecutor; |
|
47 import java.util.concurrent.TimeUnit; |
|
48 import sun.security.action.GetBooleanAction; |
|
49 import sun.security.action.GetIntegerAction; |
|
50 import sun.security.action.GetPropertyAction; |
37 import sun.security.provider.certpath.CertId; |
51 import sun.security.provider.certpath.CertId; |
38 import sun.security.provider.certpath.OCSP; |
52 import sun.security.provider.certpath.OCSP; |
39 import sun.security.provider.certpath.OCSPResponse; |
53 import sun.security.provider.certpath.OCSPResponse; |
40 import sun.security.provider.certpath.ResponderId; |
54 import sun.security.provider.certpath.ResponderId; |
41 import sun.security.util.Cache; |
55 import sun.security.util.Cache; |
42 import sun.security.x509.PKIXExtensions; |
56 import sun.security.x509.PKIXExtensions; |
43 import sun.security.x509.SerialNumber; |
57 import sun.security.x509.SerialNumber; |
44 import sun.security.action.GetBooleanAction; |
58 import sun.security.ssl.X509Authentication.X509Possession; |
45 import sun.security.action.GetIntegerAction; |
59 import static sun.security.ssl.CertStatusExtension.*; |
46 import sun.security.action.GetPropertyAction; |
|
47 |
60 |
48 final class StatusResponseManager { |
61 final class StatusResponseManager { |
49 private static final int DEFAULT_CORE_THREADS = 8; |
62 private static final int DEFAULT_CORE_THREADS = 8; |
50 private static final int DEFAULT_CACHE_SIZE = 256; |
63 private static final int DEFAULT_CACHE_SIZE = 256; |
51 private static final int DEFAULT_CACHE_LIFETIME = 3600; // seconds |
64 private static final int DEFAULT_CACHE_LIFETIME = 3600; // seconds |
52 private static final Debug debug = Debug.getInstance("ssl"); |
|
53 |
65 |
54 private final ScheduledThreadPoolExecutor threadMgr; |
66 private final ScheduledThreadPoolExecutor threadMgr; |
55 private final Cache<CertId, ResponseCacheEntry> responseCache; |
67 private final Cache<CertId, ResponseCacheEntry> responseCache; |
56 private final URI defaultResponder; |
68 private final URI defaultResponder; |
57 private final boolean respOverride; |
69 private final boolean respOverride; |
170 int size() { |
186 int size() { |
171 return responseCache.size(); |
187 return responseCache.size(); |
172 } |
188 } |
173 |
189 |
174 /** |
190 /** |
175 * Obtain the URI use by the {@code StatusResponseManager} during lookups. |
191 * Obtain the URI use by the {@code StatusResponseManager} during |
|
192 * lookups. |
|
193 * |
176 * This method takes into account not only the AIA extension from a |
194 * This method takes into account not only the AIA extension from a |
177 * certificate to be checked, but also any default URI and possible |
195 * certificate to be checked, but also any default URI and possible |
178 * override settings for the response manager. |
196 * override settings for the response manager. |
179 * |
197 * |
180 * @param cert the subject to get the responder URI from |
198 * @param cert the subject to get the responder URI from |
181 * |
199 * |
182 * @return a {@code URI} containing the address to the OCSP responder, or |
200 * @return a {@code URI} containing the address to the OCSP responder, |
183 * {@code null} if no AIA extension exists in the certificate and no |
201 * or {@code null} if no AIA extension exists in the certificate |
184 * default responder has been configured. |
202 * and no default responder has been configured. |
185 * |
203 * |
186 * @throws NullPointerException if {@code cert} is {@code null}. |
204 * @throws NullPointerException if {@code cert} is {@code null}. |
187 */ |
205 */ |
188 URI getURI(X509Certificate cert) { |
206 URI getURI(X509Certificate cert) { |
189 Objects.requireNonNull(cert); |
207 Objects.requireNonNull(cert); |
190 |
208 |
191 if (cert.getExtensionValue( |
209 if (cert.getExtensionValue( |
192 PKIXExtensions.OCSPNoCheck_Id.toString()) != null) { |
210 PKIXExtensions.OCSPNoCheck_Id.toString()) != null) { |
193 debugLog("OCSP NoCheck extension found. OCSP will be skipped"); |
211 if (SSLLogger.isOn && SSLLogger.isOn("respmgr")) { |
|
212 SSLLogger.fine( |
|
213 "OCSP NoCheck extension found. OCSP will be skipped"); |
|
214 } |
194 return null; |
215 return null; |
195 } else if (defaultResponder != null && respOverride) { |
216 } else if (defaultResponder != null && respOverride) { |
196 debugLog("Responder override: URI is " + defaultResponder); |
217 if (SSLLogger.isOn && SSLLogger.isOn("respmgr")) { |
|
218 SSLLogger.fine( |
|
219 "Responder override: URI is " + defaultResponder); |
|
220 } |
197 return defaultResponder; |
221 return defaultResponder; |
198 } else { |
222 } else { |
199 URI certURI = OCSP.getResponderURI(cert); |
223 URI certURI = OCSP.getResponderURI(cert); |
200 return (certURI != null ? certURI : defaultResponder); |
224 return (certURI != null ? certURI : defaultResponder); |
201 } |
225 } |
203 |
227 |
204 /** |
228 /** |
205 * Shutdown the thread pool |
229 * Shutdown the thread pool |
206 */ |
230 */ |
207 void shutdown() { |
231 void shutdown() { |
208 debugLog("Shutting down " + threadMgr.getActiveCount() + |
232 if (SSLLogger.isOn && SSLLogger.isOn("respmgr")) { |
209 " active threads"); |
233 SSLLogger.fine("Shutting down " + threadMgr.getActiveCount() + |
|
234 " active threads"); |
|
235 } |
210 threadMgr.shutdown(); |
236 threadMgr.shutdown(); |
211 } |
237 } |
212 |
238 |
213 /** |
239 /** |
214 * Get a list of responses for a chain of certificates. |
240 * Get a list of responses for a chain of certificates. |
215 * This will find OCSP responses from the cache, or failing that, directly |
241 * |
216 * contact the OCSP responder. It is assumed that the certificates in |
242 * This will find OCSP responses from the cache, or failing that, |
217 * the provided chain are in their proper order (from end-entity to |
243 * directly contact the OCSP responder. It is assumed that the |
218 * trust anchor). |
244 * certificates in the provided chain are in their proper order |
|
245 * (from end-entity to trust anchor). |
219 * |
246 * |
220 * @param type the type of request being made of the |
247 * @param type the type of request being made of the |
221 * {@code StatusResponseManager} |
248 * {@code StatusResponseManager} |
222 * @param request the {@code StatusRequest} from the status_request or |
249 * @param request the {@code CertStatusRequest} from the |
223 * status_request_v2 ClientHello extension. A value of {@code null} |
250 * status_request or status_request_v2 ClientHello extension. |
224 * is interpreted as providing no responder IDs or extensions. |
251 * A value of {@code null} is interpreted as providing no |
225 * @param chain an array of 2 or more certificates. Each certificate must |
252 * responder IDs or extensions. |
226 * be issued by the next certificate in the chain. |
253 * @param chain an array of 2 or more certificates. Each certificate |
|
254 * must be issued by the next certificate in the chain. |
227 * @param delay the number of time units to delay before returning |
255 * @param delay the number of time units to delay before returning |
228 * responses. |
256 * responses. |
229 * @param unit the unit of time applied to the {@code delay} parameter |
257 * @param unit the unit of time applied to the {@code delay} parameter |
230 * |
258 * |
231 * @return an unmodifiable {@code Map} containing the certificate and |
259 * @return an unmodifiable {@code Map} containing the certificate and |
232 * its usually |
260 * its usually |
233 * |
261 * |
234 * @throws SSLHandshakeException if an unsupported {@code StatusRequest} |
262 * @throws SSLHandshakeException if an unsupported |
235 * is provided. |
263 * {@code CertStatusRequest} is provided. |
236 */ |
264 */ |
237 Map<X509Certificate, byte[]> get(StatusRequestType type, |
265 Map<X509Certificate, byte[]> get(CertStatusRequestType type, |
238 StatusRequest request, X509Certificate[] chain, long delay, |
266 CertStatusRequest request, X509Certificate[] chain, long delay, |
239 TimeUnit unit) { |
267 TimeUnit unit) { |
240 Map<X509Certificate, byte[]> responseMap = new HashMap<>(); |
268 Map<X509Certificate, byte[]> responseMap = new HashMap<>(); |
241 List<OCSPFetchCall> requestList = new ArrayList<>(); |
269 List<OCSPFetchCall> requestList = new ArrayList<>(); |
242 |
270 |
243 debugLog("Beginning check: Type = " + type + ", Chain length = " + |
271 if (SSLLogger.isOn && SSLLogger.isOn("respmgr")) { |
|
272 SSLLogger.fine( |
|
273 "Beginning check: Type = " + type + ", Chain length = " + |
244 chain.length); |
274 chain.length); |
|
275 } |
245 |
276 |
246 // It is assumed that the caller has ordered the certs in the chain |
277 // It is assumed that the caller has ordered the certs in the chain |
247 // in the proper order (each certificate is issued by the next entry |
278 // in the proper order (each certificate is issued by the next entry |
248 // in the provided chain). |
279 // in the provided chain). |
249 if (chain.length < 2) { |
280 if (chain.length < 2) { |
250 return Collections.emptyMap(); |
281 return Collections.emptyMap(); |
251 } |
282 } |
252 |
283 |
253 if (type == StatusRequestType.OCSP) { |
284 if (type == CertStatusRequestType.OCSP) { |
254 try { |
285 try { |
255 // For type OCSP, we only check the end-entity certificate |
286 // For type OCSP, we only check the end-entity certificate |
256 OCSPStatusRequest ocspReq = (OCSPStatusRequest)request; |
287 OCSPStatusRequest ocspReq = (OCSPStatusRequest)request; |
257 CertId cid = new CertId(chain[1], |
288 CertId cid = new CertId(chain[1], |
258 new SerialNumber(chain[0].getSerialNumber())); |
289 new SerialNumber(chain[0].getSerialNumber())); |
262 } else { |
293 } else { |
263 StatusInfo sInfo = new StatusInfo(chain[0], cid); |
294 StatusInfo sInfo = new StatusInfo(chain[0], cid); |
264 requestList.add(new OCSPFetchCall(sInfo, ocspReq)); |
295 requestList.add(new OCSPFetchCall(sInfo, ocspReq)); |
265 } |
296 } |
266 } catch (IOException exc) { |
297 } catch (IOException exc) { |
267 debugLog("Exception during CertId creation: " + exc); |
298 if (SSLLogger.isOn && SSLLogger.isOn("respmgr")) { |
268 } |
299 SSLLogger.fine( |
269 } else if (type == StatusRequestType.OCSP_MULTI) { |
300 "Exception during CertId creation: ", exc); |
|
301 } |
|
302 } |
|
303 } else if (type == CertStatusRequestType.OCSP_MULTI) { |
270 // For type OCSP_MULTI, we check every cert in the chain that |
304 // For type OCSP_MULTI, we check every cert in the chain that |
271 // has a direct issuer at the next index. We won't have an issuer |
305 // has a direct issuer at the next index. We won't have an |
272 // certificate for the last certificate in the chain and will |
306 // issuer certificate for the last certificate in the chain |
273 // not be able to create a CertId because of that. |
307 // and will not be able to create a CertId because of that. |
274 OCSPStatusRequest ocspReq = (OCSPStatusRequest)request; |
308 OCSPStatusRequest ocspReq = (OCSPStatusRequest)request; |
275 int ctr; |
309 int ctr; |
276 for (ctr = 0; ctr < chain.length - 1; ctr++) { |
310 for (ctr = 0; ctr < chain.length - 1; ctr++) { |
277 try { |
311 try { |
278 // The cert at "ctr" is the subject cert, "ctr + 1" is the |
312 // The cert at "ctr" is the subject cert, "ctr + 1" |
279 // issuer certificate. |
313 // is the issuer certificate. |
280 CertId cid = new CertId(chain[ctr + 1], |
314 CertId cid = new CertId(chain[ctr + 1], |
281 new SerialNumber(chain[ctr].getSerialNumber())); |
315 new SerialNumber(chain[ctr].getSerialNumber())); |
282 ResponseCacheEntry cacheEntry = getFromCache(cid, ocspReq); |
316 ResponseCacheEntry cacheEntry = |
|
317 getFromCache(cid, ocspReq); |
283 if (cacheEntry != null) { |
318 if (cacheEntry != null) { |
284 responseMap.put(chain[ctr], cacheEntry.ocspBytes); |
319 responseMap.put(chain[ctr], cacheEntry.ocspBytes); |
285 } else { |
320 } else { |
286 StatusInfo sInfo = new StatusInfo(chain[ctr], cid); |
321 StatusInfo sInfo = new StatusInfo(chain[ctr], cid); |
287 requestList.add(new OCSPFetchCall(sInfo, ocspReq)); |
322 requestList.add(new OCSPFetchCall(sInfo, ocspReq)); |
288 } |
323 } |
289 } catch (IOException exc) { |
324 } catch (IOException exc) { |
290 debugLog("Exception during CertId creation: " + exc); |
325 if (SSLLogger.isOn && SSLLogger.isOn("respmgr")) { |
|
326 SSLLogger.fine( |
|
327 "Exception during CertId creation: ", exc); |
|
328 } |
291 } |
329 } |
292 } |
330 } |
293 } else { |
331 } else { |
294 debugLog("Unsupported status request type: " + type); |
332 if (SSLLogger.isOn && SSLLogger.isOn("respmgr")) { |
|
333 SSLLogger.fine("Unsupported status request type: " + type); |
|
334 } |
295 } |
335 } |
296 |
336 |
297 // If we were able to create one or more Fetches, go and run all |
337 // If we were able to create one or more Fetches, go and run all |
298 // of them in separate threads. For all the threads that completed |
338 // of them in separate threads. For all the threads that completed |
299 // in the allotted time, put those status responses into the returned |
339 // in the allotted time, put those status responses into the |
300 // Map. |
340 // returned Map. |
301 if (!requestList.isEmpty()) { |
341 if (!requestList.isEmpty()) { |
302 try { |
342 try { |
303 // Set a bunch of threads to go do the fetching |
343 // Set a bunch of threads to go do the fetching |
304 List<Future<StatusInfo>> resultList = |
344 List<Future<StatusInfo>> resultList = |
305 threadMgr.invokeAll(requestList, delay, unit); |
345 threadMgr.invokeAll(requestList, delay, unit); |
306 |
346 |
307 // Go through the Futures and from any non-cancelled task, |
347 // Go through the Futures and from any non-cancelled task, |
308 // get the bytes and attach them to the responseMap. |
348 // get the bytes and attach them to the responseMap. |
309 for (Future<StatusInfo> task : resultList) { |
349 for (Future<StatusInfo> task : resultList) { |
310 if (task.isDone()) { |
350 if (!task.isDone()) { |
311 if (!task.isCancelled()) { |
351 continue; |
312 StatusInfo info = task.get(); |
352 } |
313 if (info != null && info.responseData != null) { |
353 |
314 responseMap.put(info.cert, |
354 if (!task.isCancelled()) { |
315 info.responseData.ocspBytes); |
355 StatusInfo info = task.get(); |
316 } else { |
356 if (info != null && info.responseData != null) { |
317 debugLog("Completed task had no response data"); |
357 responseMap.put(info.cert, |
318 } |
358 info.responseData.ocspBytes); |
319 } else { |
359 } else if (SSLLogger.isOn && |
320 debugLog("Found cancelled task"); |
360 SSLLogger.isOn("respmgr")) { |
|
361 SSLLogger.fine( |
|
362 "Completed task had no response data"); |
|
363 } |
|
364 } else { |
|
365 if (SSLLogger.isOn && SSLLogger.isOn("respmgr")) { |
|
366 SSLLogger.fine("Found cancelled task"); |
321 } |
367 } |
322 } |
368 } |
323 } |
369 } |
324 } catch (InterruptedException | ExecutionException exc) { |
370 } catch (InterruptedException | ExecutionException exc) { |
325 // Not sure what else to do here |
371 // Not sure what else to do here |
326 debugLog("Exception when getting data: " + exc); |
372 if (SSLLogger.isOn && SSLLogger.isOn("respmgr")) { |
|
373 SSLLogger.fine("Exception when getting data: ", exc); |
|
374 } |
327 } |
375 } |
328 } |
376 } |
329 |
377 |
330 return Collections.unmodifiableMap(responseMap); |
378 return Collections.unmodifiableMap(responseMap); |
331 } |
379 } |
469 * @return a {@code String} representation of this object |
513 * @return a {@code String} representation of this object |
470 */ |
514 */ |
471 @Override |
515 @Override |
472 public String toString() { |
516 public String toString() { |
473 StringBuilder sb = new StringBuilder("StatusInfo:"); |
517 StringBuilder sb = new StringBuilder("StatusInfo:"); |
474 sb.append("\n\tCert: ").append(this.cert.getSubjectX500Principal()); |
518 sb.append("\n\tCert: ").append( |
|
519 this.cert.getSubjectX500Principal()); |
475 sb.append("\n\tSerial: ").append(this.cert.getSerialNumber()); |
520 sb.append("\n\tSerial: ").append(this.cert.getSerialNumber()); |
476 sb.append("\n\tResponder: ").append(this.responder); |
521 sb.append("\n\tResponder: ").append(this.responder); |
477 sb.append("\n\tResponse data: ").append(this.responseData != null ? |
522 sb.append("\n\tResponse data: ").append( |
478 (this.responseData.ocspBytes.length + " bytes") : "<NULL>"); |
523 this.responseData != null ? |
|
524 (this.responseData.ocspBytes.length + " bytes") : |
|
525 "<NULL>"); |
479 return sb.toString(); |
526 return sb.toString(); |
480 } |
527 } |
481 } |
528 } |
482 |
529 |
483 /** |
530 /** |
484 * Static nested class used as the data kept in the response cache. |
531 * Static nested class used as the data kept in the response cache. |
485 */ |
532 */ |
486 static class ResponseCacheEntry { |
533 class ResponseCacheEntry { |
487 final OCSPResponse.ResponseStatus status; |
534 final OCSPResponse.ResponseStatus status; |
488 final byte[] ocspBytes; |
535 final byte[] ocspBytes; |
489 final Date nextUpdate; |
536 final Date nextUpdate; |
490 final OCSPResponse.SingleResponse singleResp; |
537 final OCSPResponse.SingleResponse singleResp; |
491 final ResponderId respId; |
538 final ResponderId respId; |
547 public OCSPFetchCall(StatusInfo info, OCSPStatusRequest request) { |
596 public OCSPFetchCall(StatusInfo info, OCSPStatusRequest request) { |
548 statInfo = Objects.requireNonNull(info, |
597 statInfo = Objects.requireNonNull(info, |
549 "Null StatusInfo not allowed"); |
598 "Null StatusInfo not allowed"); |
550 ocspRequest = Objects.requireNonNull(request, |
599 ocspRequest = Objects.requireNonNull(request, |
551 "Null OCSPStatusRequest not allowed"); |
600 "Null OCSPStatusRequest not allowed"); |
552 extensions = ocspRequest.getExtensions(); |
601 extensions = ocspRequest.extensions; |
553 responderIds = ocspRequest.getResponderIds(); |
602 responderIds = ocspRequest.responderIds; |
554 } |
603 } |
555 |
604 |
556 /** |
605 /** |
557 * Get an OCSP response, either from the cache or from a responder. |
606 * Get an OCSP response, either from the cache or from a responder. |
558 * |
607 * |
559 * @return The StatusInfo object passed into the {@code OCSPFetchCall} |
608 * @return The StatusInfo object passed into the |
560 * constructor, with the {@code responseData} field filled in with the |
609 * {@code OCSPFetchCall} constructor, with the |
561 * response or {@code null} if no response can be obtained. |
610 * {@code responseData} field filled in with the response |
|
611 * or {@code null} if no response can be obtained. |
562 */ |
612 */ |
563 @Override |
613 @Override |
564 public StatusInfo call() { |
614 public StatusInfo call() { |
565 debugLog("Starting fetch for SN " + statInfo.cid.getSerialNumber()); |
615 if (SSLLogger.isOn && SSLLogger.isOn("respmgr")) { |
|
616 SSLLogger.fine( |
|
617 "Starting fetch for SN " + |
|
618 statInfo.cid.getSerialNumber()); |
|
619 } |
566 try { |
620 try { |
567 ResponseCacheEntry cacheEntry; |
621 ResponseCacheEntry cacheEntry; |
568 List<Extension> extsToSend; |
622 List<Extension> extsToSend; |
569 |
623 |
570 if (statInfo.responder == null) { |
624 if (statInfo.responder == null) { |
571 // If we have no URI then there's nothing to do but return |
625 // If we have no URI then there's nothing to do |
572 debugLog("Null URI detected, OCSP fetch aborted."); |
626 // but return. |
|
627 if (SSLLogger.isOn && SSLLogger.isOn("respmgr")) { |
|
628 SSLLogger.fine( |
|
629 "Null URI detected, OCSP fetch aborted"); |
|
630 } |
573 return statInfo; |
631 return statInfo; |
574 } else { |
632 } else { |
575 debugLog("Attempting fetch from " + statInfo.responder); |
633 if (SSLLogger.isOn && SSLLogger.isOn("respmgr")) { |
|
634 SSLLogger.fine( |
|
635 "Attempting fetch from " + statInfo.responder); |
|
636 } |
576 } |
637 } |
577 |
638 |
578 // If the StatusResponseManager has been configured to not |
639 // If the StatusResponseManager has been configured to not |
579 // forward extensions, then set extensions to an empty list. |
640 // forward extensions, then set extensions to an empty |
580 // We will forward the extensions unless one of two conditions |
641 // list. |
581 // occur: (1) The jdk.tls.stapling.ignoreExtensions property is |
642 // |
582 // true or (2) There is a non-empty ResponderId list. |
643 // We will forward the extensions unless one of two |
|
644 // conditions occur: |
|
645 // (1) The jdk.tls.stapling.ignoreExtensions property is |
|
646 // true, or |
|
647 // (2) There is a non-empty ResponderId list. |
|
648 // |
583 // ResponderId selection is a feature that will be |
649 // ResponderId selection is a feature that will be |
584 // supported in the future. |
650 // supported in the future. |
585 extsToSend = (ignoreExtensions || !responderIds.isEmpty()) ? |
651 extsToSend = (ignoreExtensions || !responderIds.isEmpty()) ? |
586 Collections.emptyList() : extensions; |
652 Collections.emptyList() : extensions; |
587 |
653 |
624 */ |
697 */ |
625 private void addToCache(CertId certId, ResponseCacheEntry entry) { |
698 private void addToCache(CertId certId, ResponseCacheEntry entry) { |
626 // If no cache lifetime has been set on entries then |
699 // If no cache lifetime has been set on entries then |
627 // don't cache this response if there is no nextUpdate field |
700 // don't cache this response if there is no nextUpdate field |
628 if (entry.nextUpdate == null && cacheLifetime == 0) { |
701 if (entry.nextUpdate == null && cacheLifetime == 0) { |
629 debugLog("Not caching this OCSP response"); |
702 if (SSLLogger.isOn && SSLLogger.isOn("respmgr")) { |
|
703 SSLLogger.fine("Not caching this OCSP response"); |
|
704 } |
630 } else { |
705 } else { |
631 responseCache.put(certId, entry); |
706 responseCache.put(certId, entry); |
632 debugLog("Added response for SN " + certId.getSerialNumber() + |
707 if (SSLLogger.isOn && SSLLogger.isOn("respmgr")) { |
|
708 SSLLogger.fine( |
|
709 "Added response for SN " + |
|
710 certId.getSerialNumber() + |
633 " to cache"); |
711 " to cache"); |
|
712 } |
634 } |
713 } |
635 } |
714 } |
636 |
715 |
637 /** |
716 /** |
638 * Determine the delay to use when scheduling the task that will |
717 * Determine the delay to use when scheduling the task that will |
639 * update the OCSP response. This is the shorter time between the |
718 * update the OCSP response. This is the shorter time between the |
640 * cache lifetime and the nextUpdate. If no nextUpdate is present in |
719 * cache lifetime and the nextUpdate. If no nextUpdate is present |
641 * the response, then only the cache lifetime is used. |
720 * in the response, then only the cache lifetime is used. |
642 * If cache timeouts are disabled (a zero value) and there's no |
721 * If cache timeouts are disabled (a zero value) and there's no |
643 * nextUpdate, then the entry is not cached and no rescheduling will |
722 * nextUpdate, then the entry is not cached and no rescheduling |
644 * take place. |
723 * will take place. |
645 * |
724 * |
646 * @param nextUpdate a {@code Date} object corresponding to the |
725 * @param nextUpdate a {@code Date} object corresponding to the |
647 * next update time from a SingleResponse. |
726 * next update time from a SingleResponse. |
648 * |
727 * |
649 * @return the number of seconds of delay before the next fetch |
728 * @return the number of seconds of delay before the next fetch |
665 } |
744 } |
666 |
745 |
667 return delaySec; |
746 return delaySec; |
668 } |
747 } |
669 } |
748 } |
|
749 |
|
750 static final StaplingParameters processStapling( |
|
751 ServerHandshakeContext shc) { |
|
752 StaplingParameters params = null; |
|
753 SSLExtension ext = null; |
|
754 CertStatusRequestType type = null; |
|
755 CertStatusRequest req = null; |
|
756 Map<X509Certificate, byte[]> responses; |
|
757 |
|
758 // If this feature has not been enabled, then no more processing |
|
759 // is necessary. Also we will only staple if we're doing a full |
|
760 // handshake. |
|
761 if (!shc.sslContext.isStaplingEnabled(false) || shc.isResumption) { |
|
762 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { |
|
763 SSLLogger.fine("Staping disabled or is a resumed session"); |
|
764 } |
|
765 return null; |
|
766 } |
|
767 |
|
768 // Check if the client has asserted the status_request[_v2] extension(s) |
|
769 Map<SSLExtension, SSLExtension.SSLExtensionSpec> exts = |
|
770 shc.handshakeExtensions; |
|
771 CertStatusRequestSpec statReq = (CertStatusRequestSpec)exts.get( |
|
772 SSLExtension.CH_STATUS_REQUEST); |
|
773 CertStatusRequestV2Spec statReqV2 = (CertStatusRequestV2Spec) |
|
774 exts.get(SSLExtension.CH_STATUS_REQUEST_V2); |
|
775 |
|
776 // Determine which type of stapling we are doing and assert the |
|
777 // proper extension in the server hello. |
|
778 // Favor status_request_v2 over status_request and ocsp_multi |
|
779 // over ocsp. |
|
780 // If multiple ocsp or ocsp_multi types exist, select the first |
|
781 // instance of a given type. Also since we don't support ResponderId |
|
782 // selection yet, only accept a request if the ResponderId field |
|
783 // is empty. Finally, we'll only do this in (D)TLS 1.2 or earlier. |
|
784 if (statReqV2 != null && !shc.negotiatedProtocol.useTLS13PlusSpec()) { |
|
785 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake,verbose")) { |
|
786 SSLLogger.fine("SH Processing status_request_v2 extension"); |
|
787 } |
|
788 // RFC 6961 stapling |
|
789 ext = SSLExtension.CH_STATUS_REQUEST_V2; |
|
790 int ocspIdx = -1; |
|
791 int ocspMultiIdx = -1; |
|
792 CertStatusRequest[] reqItems = statReqV2.certStatusRequests; |
|
793 for (int pos = 0; (pos < reqItems.length && |
|
794 (ocspIdx == -1 || ocspMultiIdx == -1)); pos++) { |
|
795 CertStatusRequest item = reqItems[pos]; |
|
796 CertStatusRequestType curType = |
|
797 CertStatusRequestType.valueOf(item.statusType); |
|
798 if (ocspIdx < 0 && curType == CertStatusRequestType.OCSP) { |
|
799 OCSPStatusRequest ocspReq = (OCSPStatusRequest)item; |
|
800 // We currently only accept empty responder ID lists |
|
801 // but may support them in the future |
|
802 if (ocspReq.responderIds.isEmpty()) { |
|
803 ocspIdx = pos; |
|
804 } |
|
805 } else if (ocspMultiIdx < 0 && |
|
806 curType == CertStatusRequestType.OCSP_MULTI) { |
|
807 OCSPStatusRequest ocspReq = (OCSPStatusRequest)item; |
|
808 // We currently only accept empty responder ID lists |
|
809 // but may support them in the future |
|
810 if (ocspReq.responderIds.isEmpty()) { |
|
811 ocspMultiIdx = pos; |
|
812 } |
|
813 } |
|
814 } |
|
815 if (ocspMultiIdx >= 0) { |
|
816 req = reqItems[ocspMultiIdx]; |
|
817 type = CertStatusRequestType.valueOf(req.statusType); |
|
818 } else if (ocspIdx >= 0) { |
|
819 req = reqItems[ocspIdx]; |
|
820 type = CertStatusRequestType.valueOf(req.statusType); |
|
821 } else { |
|
822 if (SSLLogger.isOn && |
|
823 SSLLogger.isOn("ssl,handshake")) { |
|
824 SSLLogger.finest("Warning: No suitable request " + |
|
825 "found in the status_request_v2 extension."); |
|
826 } |
|
827 } |
|
828 } |
|
829 |
|
830 // Only attempt to process a status_request extension if: |
|
831 // * The status_request extension is set AND |
|
832 // * either the status_request_v2 extension is not present OR |
|
833 // * none of the underlying OCSPStatusRequest structures is |
|
834 // suitable for stapling. |
|
835 // If either of the latter two bullet items is true the ext, |
|
836 // type and req variables should all be null. If any are null |
|
837 // we will try processing an asserted status_request. |
|
838 if ((statReq != null) && |
|
839 (ext == null || type == null || req == null)) { |
|
840 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake,verbose")) { |
|
841 SSLLogger.fine("SH Processing status_request extension"); |
|
842 } |
|
843 ext = SSLExtension.CH_STATUS_REQUEST; |
|
844 type = CertStatusRequestType.valueOf( |
|
845 statReq.statusRequest.statusType); |
|
846 if (type == CertStatusRequestType.OCSP) { |
|
847 // If the type is OCSP, then the request is guaranteed |
|
848 // to be OCSPStatusRequest |
|
849 OCSPStatusRequest ocspReq = |
|
850 (OCSPStatusRequest)statReq.statusRequest; |
|
851 if (ocspReq.responderIds.isEmpty()) { |
|
852 req = ocspReq; |
|
853 } else { |
|
854 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { |
|
855 SSLLogger.finest("Warning: No suitable request " + |
|
856 "found in the status_request extension."); |
|
857 } |
|
858 } |
|
859 } |
|
860 } |
|
861 |
|
862 // If, after walking through the extensions we were unable to |
|
863 // find a suitable StatusRequest, then stapling is disabled. |
|
864 // The ext, type and req variables must have been set to continue. |
|
865 if (type == null || req == null || ext == null) { |
|
866 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { |
|
867 SSLLogger.fine("No suitable status_request or " + |
|
868 "status_request_v2, stapling is disabled"); |
|
869 } |
|
870 return null; |
|
871 } |
|
872 |
|
873 // Get the cert chain since we'll need it for OCSP checking |
|
874 X509Possession x509Possession = null; |
|
875 for (SSLPossession possession : shc.handshakePossessions) { |
|
876 if (possession instanceof X509Possession) { |
|
877 x509Possession = (X509Possession)possession; |
|
878 break; |
|
879 } |
|
880 } |
|
881 |
|
882 if (x509Possession == null) { // unlikely |
|
883 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { |
|
884 SSLLogger.finest("Warning: no X.509 certificates found. " + |
|
885 "Stapling is disabled."); |
|
886 } |
|
887 return null; |
|
888 } |
|
889 |
|
890 // Get the OCSP responses from the StatusResponseManager |
|
891 X509Certificate[] certs = x509Possession.popCerts; |
|
892 StatusResponseManager statRespMgr = |
|
893 shc.sslContext.getStatusResponseManager(); |
|
894 if (statRespMgr != null) { |
|
895 // For the purposes of the fetch from the SRM, override the |
|
896 // type when it is TLS 1.3 so it always gets responses for |
|
897 // all certs it can. This should not change the type field |
|
898 // in the StaplingParameters though. |
|
899 CertStatusRequestType fetchType = |
|
900 shc.negotiatedProtocol.useTLS13PlusSpec() ? |
|
901 CertStatusRequestType.OCSP_MULTI : type; |
|
902 responses = statRespMgr.get(fetchType, req, certs, |
|
903 shc.statusRespTimeout, TimeUnit.MILLISECONDS); |
|
904 if (!responses.isEmpty()) { |
|
905 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { |
|
906 SSLLogger.finest("Response manager returned " + |
|
907 responses.size() + " entries."); |
|
908 } |
|
909 // If this RFC 6066-style stapling (SSL cert only) then the |
|
910 // response cannot be zero length |
|
911 if (type == CertStatusRequestType.OCSP) { |
|
912 byte[] respDER = responses.get(certs[0]); |
|
913 if (respDER == null || respDER.length <= 0) { |
|
914 if (SSLLogger.isOn && |
|
915 SSLLogger.isOn("ssl,handshake")) { |
|
916 SSLLogger.finest("Warning: Null or zero-length " + |
|
917 "response found for leaf certificate. " + |
|
918 "Stapling is disabled."); |
|
919 } |
|
920 return null; |
|
921 } |
|
922 } |
|
923 params = new StaplingParameters(ext, type, req, responses); |
|
924 } else { |
|
925 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { |
|
926 SSLLogger.finest("Warning: no OCSP responses obtained. " + |
|
927 "Stapling is disabled."); |
|
928 } |
|
929 } |
|
930 } else { |
|
931 // This should not happen, but if lazy initialization of the |
|
932 // StatusResponseManager doesn't occur we should turn off stapling. |
|
933 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { |
|
934 SSLLogger.finest("Warning: lazy initialization " + |
|
935 "of the StatusResponseManager failed. " + |
|
936 "Stapling is disabled."); |
|
937 } |
|
938 params = null; |
|
939 } |
|
940 |
|
941 return params; |
|
942 } |
|
943 |
|
944 /** |
|
945 * Inner class used to hold stapling parameters needed by the handshaker |
|
946 * when stapling is active. |
|
947 */ |
|
948 static final class StaplingParameters { |
|
949 final SSLExtension statusRespExt; |
|
950 final CertStatusRequestType statReqType; |
|
951 final CertStatusRequest statReqData; |
|
952 final Map<X509Certificate, byte[]> responseMap; |
|
953 |
|
954 StaplingParameters(SSLExtension ext, CertStatusRequestType type, |
|
955 CertStatusRequest req, Map<X509Certificate, byte[]> responses) { |
|
956 statusRespExt = ext; |
|
957 statReqType = type; |
|
958 statReqData = req; |
|
959 responseMap = responses; |
|
960 } |
|
961 } |
670 } |
962 } |
|
963 |