1 /* 2 * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 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 23 * questions. 24 */ 25 26 package sun.security.ssl; 27 28 import java.io.IOException; 29 import java.net.URI; 30 import java.net.URISyntaxException; 31 import java.security.AccessController; 32 import java.security.cert.X509Certificate; 33 import java.security.cert.Extension; 34 import java.util.*; 35 import java.util.concurrent.*; 36 37 import sun.security.provider.certpath.CertId; 38 import sun.security.provider.certpath.OCSP; 39 import sun.security.provider.certpath.OCSPResponse; 40 import sun.security.provider.certpath.ResponderId; 41 import sun.security.util.Cache; 42 import sun.security.x509.PKIXExtensions; 43 import sun.security.x509.SerialNumber; 44 import sun.security.action.GetBooleanAction; 45 import sun.security.action.GetIntegerAction; 46 import sun.security.action.GetPropertyAction; 47 48 final class StatusResponseManager { 49 private static final int DEFAULT_CORE_THREADS = 8; 50 private static final int DEFAULT_CACHE_SIZE = 256; 51 private static final int DEFAULT_CACHE_LIFETIME = 3600; // seconds 52 private static final Debug debug = Debug.getInstance("ssl"); 53 54 private final ScheduledThreadPoolExecutor threadMgr; 55 private final Cache<CertId, ResponseCacheEntry> responseCache; 56 private final URI defaultResponder; 57 private final boolean respOverride; 58 private final int cacheCapacity; 59 private final int cacheLifetime; 60 private final boolean ignoreExtensions; 61 62 /** 63 * Create a StatusResponseManager with default parameters. 64 */ 65 StatusResponseManager() { 66 int cap = AccessController.doPrivileged( 67 new GetIntegerAction("jdk.tls.stapling.cacheSize", 68 DEFAULT_CACHE_SIZE)); 69 cacheCapacity = cap > 0 ? cap : 0; 70 71 int life = AccessController.doPrivileged( 72 new GetIntegerAction("jdk.tls.stapling.cacheLifetime", 82 } catch (URISyntaxException urise) { 83 tmpURI = null; 84 } 85 defaultResponder = tmpURI; 86 87 respOverride = AccessController.doPrivileged( 88 new GetBooleanAction("jdk.tls.stapling.responderOverride")); 89 ignoreExtensions = AccessController.doPrivileged( 90 new GetBooleanAction("jdk.tls.stapling.ignoreExtensions")); 91 92 threadMgr = new ScheduledThreadPoolExecutor(DEFAULT_CORE_THREADS, 93 new ThreadFactory() { 94 @Override 95 public Thread newThread(Runnable r) { 96 Thread t = Executors.defaultThreadFactory().newThread(r); 97 t.setDaemon(true); 98 return t; 99 } 100 }, new ThreadPoolExecutor.DiscardPolicy()); 101 threadMgr.setExecuteExistingDelayedTasksAfterShutdownPolicy(false); 102 threadMgr.setContinueExistingPeriodicTasksAfterShutdownPolicy(false); 103 threadMgr.setKeepAliveTime(5000, TimeUnit.MILLISECONDS); 104 threadMgr.allowCoreThreadTimeOut(true); 105 responseCache = Cache.newSoftMemoryCache(cacheCapacity, cacheLifetime); 106 } 107 108 /** 109 * Get the current cache lifetime setting 110 * 111 * @return the current cache lifetime value 112 */ 113 int getCacheLifetime() { 114 return cacheLifetime; 115 } 116 117 /** 118 * Get the current maximum cache size. 119 * 120 * @return the current maximum cache size 121 */ 122 int getCacheCapacity() { 123 return cacheCapacity; 124 } 125 130 * it has not been set. 131 */ 132 URI getDefaultResponder() { 133 return defaultResponder; 134 } 135 136 /** 137 * Get the URI override setting 138 * 139 * @return {@code true} if URI override has been set, {@code false} 140 * otherwise. 141 */ 142 boolean getURIOverride() { 143 return respOverride; 144 } 145 146 /** 147 * Get the ignore extensions setting. 148 * 149 * @return {@code true} if the {@code StatusResponseManager} will not 150 * pass OCSP Extensions in the TLS {@code status_request[_v2]} extensions, 151 * {@code false} if extensions will be passed (the default). 152 */ 153 boolean getIgnoreExtensions() { 154 return ignoreExtensions; 155 } 156 157 /** 158 * Clear the status response cache 159 */ 160 void clear() { 161 debugLog("Clearing response cache"); 162 responseCache.clear(); 163 } 164 165 /** 166 * Returns the number of currently valid objects in the response cache. 167 * 168 * @return the number of valid objects in the response cache. 169 */ 170 int size() { 171 return responseCache.size(); 172 } 173 174 /** 175 * Obtain the URI use by the {@code StatusResponseManager} during lookups. 176 * This method takes into account not only the AIA extension from a 177 * certificate to be checked, but also any default URI and possible 178 * override settings for the response manager. 179 * 180 * @param cert the subject to get the responder URI from 181 * 182 * @return a {@code URI} containing the address to the OCSP responder, or 183 * {@code null} if no AIA extension exists in the certificate and no 184 * default responder has been configured. 185 * 186 * @throws NullPointerException if {@code cert} is {@code null}. 187 */ 188 URI getURI(X509Certificate cert) { 189 Objects.requireNonNull(cert); 190 191 if (cert.getExtensionValue( 192 PKIXExtensions.OCSPNoCheck_Id.toString()) != null) { 193 debugLog("OCSP NoCheck extension found. OCSP will be skipped"); 194 return null; 195 } else if (defaultResponder != null && respOverride) { 196 debugLog("Responder override: URI is " + defaultResponder); 197 return defaultResponder; 198 } else { 199 URI certURI = OCSP.getResponderURI(cert); 200 return (certURI != null ? certURI : defaultResponder); 201 } 202 } 203 204 /** 205 * Shutdown the thread pool 206 */ 207 void shutdown() { 208 debugLog("Shutting down " + threadMgr.getActiveCount() + 209 " active threads"); 210 threadMgr.shutdown(); 211 } 212 213 /** 214 * Get a list of responses for a chain of certificates. 215 * This will find OCSP responses from the cache, or failing that, directly 216 * contact the OCSP responder. It is assumed that the certificates in 217 * the provided chain are in their proper order (from end-entity to 218 * trust anchor). 219 * 220 * @param type the type of request being made of the 221 * {@code StatusResponseManager} 222 * @param request the {@code StatusRequest} from the status_request or 223 * status_request_v2 ClientHello extension. A value of {@code null} 224 * is interpreted as providing no responder IDs or extensions. 225 * @param chain an array of 2 or more certificates. Each certificate must 226 * be issued by the next certificate in the chain. 227 * @param delay the number of time units to delay before returning 228 * responses. 229 * @param unit the unit of time applied to the {@code delay} parameter 230 * 231 * @return an unmodifiable {@code Map} containing the certificate and 232 * its usually 233 * 234 * @throws SSLHandshakeException if an unsupported {@code StatusRequest} 235 * is provided. 236 */ 237 Map<X509Certificate, byte[]> get(StatusRequestType type, 238 StatusRequest request, X509Certificate[] chain, long delay, 239 TimeUnit unit) { 240 Map<X509Certificate, byte[]> responseMap = new HashMap<>(); 241 List<OCSPFetchCall> requestList = new ArrayList<>(); 242 243 debugLog("Beginning check: Type = " + type + ", Chain length = " + 244 chain.length); 245 246 // 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 248 // in the provided chain). 249 if (chain.length < 2) { 250 return Collections.emptyMap(); 251 } 252 253 if (type == StatusRequestType.OCSP) { 254 try { 255 // For type OCSP, we only check the end-entity certificate 256 OCSPStatusRequest ocspReq = (OCSPStatusRequest)request; 257 CertId cid = new CertId(chain[1], 258 new SerialNumber(chain[0].getSerialNumber())); 259 ResponseCacheEntry cacheEntry = getFromCache(cid, ocspReq); 260 if (cacheEntry != null) { 261 responseMap.put(chain[0], cacheEntry.ocspBytes); 262 } else { 263 StatusInfo sInfo = new StatusInfo(chain[0], cid); 264 requestList.add(new OCSPFetchCall(sInfo, ocspReq)); 265 } 266 } catch (IOException exc) { 267 debugLog("Exception during CertId creation: " + exc); 268 } 269 } else if (type == StatusRequestType.OCSP_MULTI) { 270 // 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 272 // certificate for the last certificate in the chain and will 273 // not be able to create a CertId because of that. 274 OCSPStatusRequest ocspReq = (OCSPStatusRequest)request; 275 int ctr; 276 for (ctr = 0; ctr < chain.length - 1; ctr++) { 277 try { 278 // The cert at "ctr" is the subject cert, "ctr + 1" is the 279 // issuer certificate. 280 CertId cid = new CertId(chain[ctr + 1], 281 new SerialNumber(chain[ctr].getSerialNumber())); 282 ResponseCacheEntry cacheEntry = getFromCache(cid, ocspReq); 283 if (cacheEntry != null) { 284 responseMap.put(chain[ctr], cacheEntry.ocspBytes); 285 } else { 286 StatusInfo sInfo = new StatusInfo(chain[ctr], cid); 287 requestList.add(new OCSPFetchCall(sInfo, ocspReq)); 288 } 289 } catch (IOException exc) { 290 debugLog("Exception during CertId creation: " + exc); 291 } 292 } 293 } else { 294 debugLog("Unsupported status request type: " + type); 295 } 296 297 // 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 299 // in the allotted time, put those status responses into the returned 300 // Map. 301 if (!requestList.isEmpty()) { 302 try { 303 // Set a bunch of threads to go do the fetching 304 List<Future<StatusInfo>> resultList = 305 threadMgr.invokeAll(requestList, delay, unit); 306 307 // Go through the Futures and from any non-cancelled task, 308 // get the bytes and attach them to the responseMap. 309 for (Future<StatusInfo> task : resultList) { 310 if (task.isDone()) { 311 if (!task.isCancelled()) { 312 StatusInfo info = task.get(); 313 if (info != null && info.responseData != null) { 314 responseMap.put(info.cert, 315 info.responseData.ocspBytes); 316 } else { 317 debugLog("Completed task had no response data"); 318 } 319 } else { 320 debugLog("Found cancelled task"); 321 } 322 } 323 } 324 } catch (InterruptedException | ExecutionException exc) { 325 // Not sure what else to do here 326 debugLog("Exception when getting data: " + exc); 327 } 328 } 329 330 return Collections.unmodifiableMap(responseMap); 331 } 332 333 /** 334 * Check the cache for a given {@code CertId}. 335 * 336 * @param cid the CertId of the response to look up 337 * @param ocspRequest the OCSP request structure sent by the client 338 * in the TLS status_request[_v2] hello extension. 339 * 340 * @return the {@code ResponseCacheEntry} for a specific CertId, or 341 * {@code null} if it is not found or a nonce extension has been 342 * requested by the caller. 343 */ 344 private ResponseCacheEntry getFromCache(CertId cid, 345 OCSPStatusRequest ocspRequest) { 346 // Determine if the nonce extension is present in the request. If 347 // so, then do not attempt to retrieve the response from the cache. 348 for (Extension ext : ocspRequest.getExtensions()) { 349 if (ext.getId().equals(PKIXExtensions.OCSPNonce_Id.toString())) { 350 debugLog("Nonce extension found, skipping cache check"); 351 return null; 352 } 353 } 354 355 ResponseCacheEntry respEntry = responseCache.get(cid); 356 357 // If the response entry has a nextUpdate and it has expired 358 // before the cache expiration, purge it from the cache 359 // and do not return it as a cache hit. 360 if (respEntry != null && respEntry.nextUpdate != null && 361 respEntry.nextUpdate.before(new Date())) { 362 debugLog("nextUpdate threshold exceeded, purging from cache"); 363 respEntry = null; 364 } 365 366 debugLog("Check cache for SN" + cid.getSerialNumber() + ": " + 367 (respEntry != null ? "HIT" : "MISS")); 368 return respEntry; 369 } 370 371 @Override 372 public String toString() { 373 StringBuilder sb = new StringBuilder("StatusResponseManager: "); 374 375 sb.append("Core threads: ").append(threadMgr.getCorePoolSize()); 376 sb.append(", Cache timeout: "); 377 if (cacheLifetime > 0) { 378 sb.append(cacheLifetime).append(" seconds"); 379 } else { 380 sb.append(" indefinite"); 381 } 382 383 sb.append(", Cache MaxSize: "); 384 if (cacheCapacity > 0) { 385 sb.append(cacheCapacity).append(" items"); 386 } else { 387 sb.append(" unbounded"); 388 } 389 390 sb.append(", Default URI: "); 391 if (defaultResponder != null) { 392 sb.append(defaultResponder); 393 } else { 394 sb.append("NONE"); 395 } 396 397 return sb.toString(); 398 } 399 400 /** 401 * Log messages through the SSL Debug facility. 402 * 403 * @param message the message to be displayed 404 */ 405 static void debugLog(String message) { 406 if (debug != null && Debug.isOn("respmgr")) { 407 StringBuilder sb = new StringBuilder(); 408 sb.append("[").append(Thread.currentThread().getName()); 409 sb.append("] ").append(message); 410 System.out.println(sb.toString()); 411 } 412 } 413 414 /** 415 * Inner class used to group request and response data. 416 */ 417 class StatusInfo { 418 final X509Certificate cert; 419 final CertId cid; 420 final URI responder; 421 ResponseCacheEntry responseData; 422 423 /** 424 * Create a StatusInfo object from certificate data. 425 * 426 * @param subjectCert the certificate to be checked for revocation 427 * @param issuerCert the issuer of the {@code subjectCert} 428 * 429 * @throws IOException if CertId creation from the certificates fails 430 */ 431 StatusInfo(X509Certificate subjectCert, X509Certificate issuerCert) 432 throws IOException { 433 this(subjectCert, new CertId(issuerCert, 434 new SerialNumber(subjectCert.getSerialNumber()))); 435 } 436 437 /** 438 * Create a StatusInfo object from an existing subject certificate 439 * and its corresponding CertId. 440 * 441 * @param subjectCert the certificate to be checked for revocation 442 * @param cid the CertId for {@code subjectCert} 443 */ 444 StatusInfo(X509Certificate subjectCert, CertId certId) { 445 cert = subjectCert; 446 cid = certId; 447 responder = getURI(cert); 448 responseData = null; 449 } 454 * responseData and extensions fields, which should not persist 455 * in a rescheduled fetch. 456 * 457 * @param orig the original {@code StatusInfo} 458 */ 459 StatusInfo(StatusInfo orig) { 460 this.cert = orig.cert; 461 this.cid = orig.cid; 462 this.responder = orig.responder; 463 this.responseData = null; 464 } 465 466 /** 467 * Return a String representation of the {@code StatusInfo} 468 * 469 * @return a {@code String} representation of this object 470 */ 471 @Override 472 public String toString() { 473 StringBuilder sb = new StringBuilder("StatusInfo:"); 474 sb.append("\n\tCert: ").append(this.cert.getSubjectX500Principal()); 475 sb.append("\n\tSerial: ").append(this.cert.getSerialNumber()); 476 sb.append("\n\tResponder: ").append(this.responder); 477 sb.append("\n\tResponse data: ").append(this.responseData != null ? 478 (this.responseData.ocspBytes.length + " bytes") : "<NULL>"); 479 return sb.toString(); 480 } 481 } 482 483 /** 484 * Static nested class used as the data kept in the response cache. 485 */ 486 static class ResponseCacheEntry { 487 final OCSPResponse.ResponseStatus status; 488 final byte[] ocspBytes; 489 final Date nextUpdate; 490 final OCSPResponse.SingleResponse singleResp; 491 final ResponderId respId; 492 493 /** 494 * Create a new cache entry from the raw bytes of the response 495 * 496 * @param responseBytes the DER encoding for the OCSP response 497 * 498 * @throws IOException if an {@code OCSPResponse} cannot be created from 499 * the encoded bytes. 500 */ 501 ResponseCacheEntry(byte[] responseBytes, CertId cid) 502 throws IOException { 503 Objects.requireNonNull(responseBytes, 504 "Non-null responseBytes required"); 505 Objects.requireNonNull(cid, "Non-null Cert ID required"); 506 507 ocspBytes = responseBytes.clone(); 508 OCSPResponse oResp = new OCSPResponse(ocspBytes); 509 status = oResp.getResponseStatus(); 510 respId = oResp.getResponderId(); 511 singleResp = oResp.getSingleResponse(cid); 512 if (status == OCSPResponse.ResponseStatus.SUCCESSFUL) { 513 if (singleResp != null) { 514 // Pull out the nextUpdate field in advance because the 515 // Date is cloned. 516 nextUpdate = singleResp.getNextUpdate(); 517 } else { 518 throw new IOException("Unable to find SingleResponse for " + 519 "SN " + cid.getSerialNumber()); 520 } 521 } else { 522 nextUpdate = null; 523 } 524 } 525 } 526 527 /** 528 * Inner Callable class that does the actual work of looking up OCSP 529 * responses, first looking at the cache and doing OCSP requests if 530 * a cache miss occurs. 531 */ 532 class OCSPFetchCall implements Callable<StatusInfo> { 533 StatusInfo statInfo; 534 OCSPStatusRequest ocspRequest; 535 List<Extension> extensions; 536 List<ResponderId> responderIds; 537 538 /** 539 * A constructor that builds the OCSPFetchCall from the provided 540 * StatusInfo and information from the status_request[_v2] extension. 541 * 542 * @param info the {@code StatusInfo} containing the subject 543 * certificate, CertId, and other supplemental info. 544 * @param request the {@code OCSPStatusRequest} containing any 545 * responder IDs and extensions. 546 */ 547 public OCSPFetchCall(StatusInfo info, OCSPStatusRequest request) { 548 statInfo = Objects.requireNonNull(info, 549 "Null StatusInfo not allowed"); 550 ocspRequest = Objects.requireNonNull(request, 551 "Null OCSPStatusRequest not allowed"); 552 extensions = ocspRequest.getExtensions(); 553 responderIds = ocspRequest.getResponderIds(); 554 } 555 556 /** 557 * Get an OCSP response, either from the cache or from a responder. 558 * 559 * @return The StatusInfo object passed into the {@code OCSPFetchCall} 560 * constructor, with the {@code responseData} field filled in with the 561 * response or {@code null} if no response can be obtained. 562 */ 563 @Override 564 public StatusInfo call() { 565 debugLog("Starting fetch for SN " + statInfo.cid.getSerialNumber()); 566 try { 567 ResponseCacheEntry cacheEntry; 568 List<Extension> extsToSend; 569 570 if (statInfo.responder == null) { 571 // If we have no URI then there's nothing to do but return 572 debugLog("Null URI detected, OCSP fetch aborted."); 573 return statInfo; 574 } else { 575 debugLog("Attempting fetch from " + statInfo.responder); 576 } 577 578 // If the StatusResponseManager has been configured to not 579 // forward extensions, then set extensions to an empty list. 580 // We will forward the extensions unless one of two conditions 581 // occur: (1) The jdk.tls.stapling.ignoreExtensions property is 582 // true or (2) There is a non-empty ResponderId list. 583 // ResponderId selection is a feature that will be 584 // supported in the future. 585 extsToSend = (ignoreExtensions || !responderIds.isEmpty()) ? 586 Collections.emptyList() : extensions; 587 588 byte[] respBytes = OCSP.getOCSPBytes( 589 Collections.singletonList(statInfo.cid), 590 statInfo.responder, extsToSend); 591 592 if (respBytes != null) { 593 // Place the data into the response cache 594 cacheEntry = new ResponseCacheEntry(respBytes, 595 statInfo.cid); 596 597 // Get the response status and act on it appropriately 598 debugLog("OCSP Status: " + cacheEntry.status + 599 " (" + respBytes.length + " bytes)"); 600 if (cacheEntry.status == 601 OCSPResponse.ResponseStatus.SUCCESSFUL) { 602 // Set the response in the returned StatusInfo 603 statInfo.responseData = cacheEntry; 604 605 // Add the response to the cache (if applicable) 606 addToCache(statInfo.cid, cacheEntry); 607 } 608 } else { 609 debugLog("No data returned from OCSP Responder"); 610 } 611 } catch (IOException ioe) { 612 debugLog("Caught exception: " + ioe); 613 } 614 615 return statInfo; 616 } 617 618 /** 619 * Add a response to the cache. 620 * 621 * @param certId The {@code CertId} for the OCSP response 622 * @param entry A cache entry containing the response bytes and 623 * the {@code OCSPResponse} built from those bytes. 624 */ 625 private void addToCache(CertId certId, ResponseCacheEntry entry) { 626 // If no cache lifetime has been set on entries then 627 // don't cache this response if there is no nextUpdate field 628 if (entry.nextUpdate == null && cacheLifetime == 0) { 629 debugLog("Not caching this OCSP response"); 630 } else { 631 responseCache.put(certId, entry); 632 debugLog("Added response for SN " + certId.getSerialNumber() + 633 " to cache"); 634 } 635 } 636 637 /** 638 * Determine the delay to use when scheduling the task that will 639 * update the OCSP response. This is the shorter time between the 640 * cache lifetime and the nextUpdate. If no nextUpdate is present in 641 * the response, then only the cache lifetime is used. 642 * If cache timeouts are disabled (a zero value) and there's no 643 * nextUpdate, then the entry is not cached and no rescheduling will 644 * take place. 645 * 646 * @param nextUpdate a {@code Date} object corresponding to the 647 * next update time from a SingleResponse. 648 * 649 * @return the number of seconds of delay before the next fetch 650 * should be executed. A zero value means that the fetch 651 * should happen immediately, while a value less than zero 652 * indicates no rescheduling should be done. 653 */ 654 private long getNextTaskDelay(Date nextUpdate) { 655 long delaySec; 656 int lifetime = getCacheLifetime(); 657 658 if (nextUpdate != null) { 659 long nuDiffSec = (nextUpdate.getTime() - 660 System.currentTimeMillis()) / 1000; 661 delaySec = lifetime > 0 ? Long.min(nuDiffSec, lifetime) : 662 nuDiffSec; 663 } else { 664 delaySec = lifetime > 0 ? lifetime : -1; 665 } 666 667 return delaySec; 668 } 669 } 670 } | 1 /* 2 * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 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 23 * questions. 24 */ 25 package sun.security.ssl; 26 27 import java.io.IOException; 28 import java.net.URI; 29 import java.net.URISyntaxException; 30 import java.security.AccessController; 31 import java.security.cert.Extension; 32 import java.security.cert.X509Certificate; 33 import java.util.ArrayList; 34 import java.util.Collections; 35 import java.util.Date; 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; 51 import sun.security.provider.certpath.CertId; 52 import sun.security.provider.certpath.OCSP; 53 import sun.security.provider.certpath.OCSPResponse; 54 import sun.security.provider.certpath.ResponderId; 55 import sun.security.util.Cache; 56 import sun.security.x509.PKIXExtensions; 57 import sun.security.x509.SerialNumber; 58 import sun.security.ssl.X509Authentication.X509Possession; 59 import static sun.security.ssl.CertStatusExtension.*; 60 61 final class StatusResponseManager { 62 private static final int DEFAULT_CORE_THREADS = 8; 63 private static final int DEFAULT_CACHE_SIZE = 256; 64 private static final int DEFAULT_CACHE_LIFETIME = 3600; // seconds 65 66 private final ScheduledThreadPoolExecutor threadMgr; 67 private final Cache<CertId, ResponseCacheEntry> responseCache; 68 private final URI defaultResponder; 69 private final boolean respOverride; 70 private final int cacheCapacity; 71 private final int cacheLifetime; 72 private final boolean ignoreExtensions; 73 74 /** 75 * Create a StatusResponseManager with default parameters. 76 */ 77 StatusResponseManager() { 78 int cap = AccessController.doPrivileged( 79 new GetIntegerAction("jdk.tls.stapling.cacheSize", 80 DEFAULT_CACHE_SIZE)); 81 cacheCapacity = cap > 0 ? cap : 0; 82 83 int life = AccessController.doPrivileged( 84 new GetIntegerAction("jdk.tls.stapling.cacheLifetime", 94 } catch (URISyntaxException urise) { 95 tmpURI = null; 96 } 97 defaultResponder = tmpURI; 98 99 respOverride = AccessController.doPrivileged( 100 new GetBooleanAction("jdk.tls.stapling.responderOverride")); 101 ignoreExtensions = AccessController.doPrivileged( 102 new GetBooleanAction("jdk.tls.stapling.ignoreExtensions")); 103 104 threadMgr = new ScheduledThreadPoolExecutor(DEFAULT_CORE_THREADS, 105 new ThreadFactory() { 106 @Override 107 public Thread newThread(Runnable r) { 108 Thread t = Executors.defaultThreadFactory().newThread(r); 109 t.setDaemon(true); 110 return t; 111 } 112 }, new ThreadPoolExecutor.DiscardPolicy()); 113 threadMgr.setExecuteExistingDelayedTasksAfterShutdownPolicy(false); 114 threadMgr.setContinueExistingPeriodicTasksAfterShutdownPolicy( 115 false); 116 threadMgr.setKeepAliveTime(5000, TimeUnit.MILLISECONDS); 117 threadMgr.allowCoreThreadTimeOut(true); 118 responseCache = Cache.newSoftMemoryCache( 119 cacheCapacity, cacheLifetime); 120 } 121 122 /** 123 * Get the current cache lifetime setting 124 * 125 * @return the current cache lifetime value 126 */ 127 int getCacheLifetime() { 128 return cacheLifetime; 129 } 130 131 /** 132 * Get the current maximum cache size. 133 * 134 * @return the current maximum cache size 135 */ 136 int getCacheCapacity() { 137 return cacheCapacity; 138 } 139 144 * it has not been set. 145 */ 146 URI getDefaultResponder() { 147 return defaultResponder; 148 } 149 150 /** 151 * Get the URI override setting 152 * 153 * @return {@code true} if URI override has been set, {@code false} 154 * otherwise. 155 */ 156 boolean getURIOverride() { 157 return respOverride; 158 } 159 160 /** 161 * Get the ignore extensions setting. 162 * 163 * @return {@code true} if the {@code StatusResponseManager} will not 164 * pass OCSP Extensions in the TLS {@code status_request[_v2]} 165 * extensions, {@code false} if extensions will be passed (the default). 166 */ 167 boolean getIgnoreExtensions() { 168 return ignoreExtensions; 169 } 170 171 /** 172 * Clear the status response cache 173 */ 174 void clear() { 175 if (SSLLogger.isOn && SSLLogger.isOn("respmgr")) { 176 SSLLogger.fine("Clearing response cache"); 177 } 178 responseCache.clear(); 179 } 180 181 /** 182 * Returns the number of currently valid objects in the response cache. 183 * 184 * @return the number of valid objects in the response cache. 185 */ 186 int size() { 187 return responseCache.size(); 188 } 189 190 /** 191 * Obtain the URI use by the {@code StatusResponseManager} during 192 * lookups. 193 * 194 * This method takes into account not only the AIA extension from a 195 * certificate to be checked, but also any default URI and possible 196 * override settings for the response manager. 197 * 198 * @param cert the subject to get the responder URI from 199 * 200 * @return a {@code URI} containing the address to the OCSP responder, 201 * or {@code null} if no AIA extension exists in the certificate 202 * and no default responder has been configured. 203 * 204 * @throws NullPointerException if {@code cert} is {@code null}. 205 */ 206 URI getURI(X509Certificate cert) { 207 Objects.requireNonNull(cert); 208 209 if (cert.getExtensionValue( 210 PKIXExtensions.OCSPNoCheck_Id.toString()) != null) { 211 if (SSLLogger.isOn && SSLLogger.isOn("respmgr")) { 212 SSLLogger.fine( 213 "OCSP NoCheck extension found. OCSP will be skipped"); 214 } 215 return null; 216 } else if (defaultResponder != null && respOverride) { 217 if (SSLLogger.isOn && SSLLogger.isOn("respmgr")) { 218 SSLLogger.fine( 219 "Responder override: URI is " + defaultResponder); 220 } 221 return defaultResponder; 222 } else { 223 URI certURI = OCSP.getResponderURI(cert); 224 return (certURI != null ? certURI : defaultResponder); 225 } 226 } 227 228 /** 229 * Shutdown the thread pool 230 */ 231 void shutdown() { 232 if (SSLLogger.isOn && SSLLogger.isOn("respmgr")) { 233 SSLLogger.fine("Shutting down " + threadMgr.getActiveCount() + 234 " active threads"); 235 } 236 threadMgr.shutdown(); 237 } 238 239 /** 240 * Get a list of responses for a chain of certificates. 241 * 242 * This will find OCSP responses from the cache, or failing that, 243 * directly contact the OCSP responder. It is assumed that the 244 * certificates in the provided chain are in their proper order 245 * (from end-entity to trust anchor). 246 * 247 * @param type the type of request being made of the 248 * {@code StatusResponseManager} 249 * @param request the {@code CertStatusRequest} from the 250 * status_request or status_request_v2 ClientHello extension. 251 * A value of {@code null} is interpreted as providing no 252 * responder IDs or extensions. 253 * @param chain an array of 2 or more certificates. Each certificate 254 * must be issued by the next certificate in the chain. 255 * @param delay the number of time units to delay before returning 256 * responses. 257 * @param unit the unit of time applied to the {@code delay} parameter 258 * 259 * @return an unmodifiable {@code Map} containing the certificate and 260 * its usually 261 * 262 * @throws SSLHandshakeException if an unsupported 263 * {@code CertStatusRequest} is provided. 264 */ 265 Map<X509Certificate, byte[]> get(CertStatusRequestType type, 266 CertStatusRequest request, X509Certificate[] chain, long delay, 267 TimeUnit unit) { 268 Map<X509Certificate, byte[]> responseMap = new HashMap<>(); 269 List<OCSPFetchCall> requestList = new ArrayList<>(); 270 271 if (SSLLogger.isOn && SSLLogger.isOn("respmgr")) { 272 SSLLogger.fine( 273 "Beginning check: Type = " + type + ", Chain length = " + 274 chain.length); 275 } 276 277 // It is assumed that the caller has ordered the certs in the chain 278 // in the proper order (each certificate is issued by the next entry 279 // in the provided chain). 280 if (chain.length < 2) { 281 return Collections.emptyMap(); 282 } 283 284 if (type == CertStatusRequestType.OCSP) { 285 try { 286 // For type OCSP, we only check the end-entity certificate 287 OCSPStatusRequest ocspReq = (OCSPStatusRequest)request; 288 CertId cid = new CertId(chain[1], 289 new SerialNumber(chain[0].getSerialNumber())); 290 ResponseCacheEntry cacheEntry = getFromCache(cid, ocspReq); 291 if (cacheEntry != null) { 292 responseMap.put(chain[0], cacheEntry.ocspBytes); 293 } else { 294 StatusInfo sInfo = new StatusInfo(chain[0], cid); 295 requestList.add(new OCSPFetchCall(sInfo, ocspReq)); 296 } 297 } catch (IOException exc) { 298 if (SSLLogger.isOn && SSLLogger.isOn("respmgr")) { 299 SSLLogger.fine( 300 "Exception during CertId creation: ", exc); 301 } 302 } 303 } else if (type == CertStatusRequestType.OCSP_MULTI) { 304 // For type OCSP_MULTI, we check every cert in the chain that 305 // has a direct issuer at the next index. We won't have an 306 // issuer certificate for the last certificate in the chain 307 // and will not be able to create a CertId because of that. 308 OCSPStatusRequest ocspReq = (OCSPStatusRequest)request; 309 int ctr; 310 for (ctr = 0; ctr < chain.length - 1; ctr++) { 311 try { 312 // The cert at "ctr" is the subject cert, "ctr + 1" 313 // is the issuer certificate. 314 CertId cid = new CertId(chain[ctr + 1], 315 new SerialNumber(chain[ctr].getSerialNumber())); 316 ResponseCacheEntry cacheEntry = 317 getFromCache(cid, ocspReq); 318 if (cacheEntry != null) { 319 responseMap.put(chain[ctr], cacheEntry.ocspBytes); 320 } else { 321 StatusInfo sInfo = new StatusInfo(chain[ctr], cid); 322 requestList.add(new OCSPFetchCall(sInfo, ocspReq)); 323 } 324 } catch (IOException exc) { 325 if (SSLLogger.isOn && SSLLogger.isOn("respmgr")) { 326 SSLLogger.fine( 327 "Exception during CertId creation: ", exc); 328 } 329 } 330 } 331 } else { 332 if (SSLLogger.isOn && SSLLogger.isOn("respmgr")) { 333 SSLLogger.fine("Unsupported status request type: " + type); 334 } 335 } 336 337 // If we were able to create one or more Fetches, go and run all 338 // of them in separate threads. For all the threads that completed 339 // in the allotted time, put those status responses into the 340 // returned Map. 341 if (!requestList.isEmpty()) { 342 try { 343 // Set a bunch of threads to go do the fetching 344 List<Future<StatusInfo>> resultList = 345 threadMgr.invokeAll(requestList, delay, unit); 346 347 // Go through the Futures and from any non-cancelled task, 348 // get the bytes and attach them to the responseMap. 349 for (Future<StatusInfo> task : resultList) { 350 if (!task.isDone()) { 351 continue; 352 } 353 354 if (!task.isCancelled()) { 355 StatusInfo info = task.get(); 356 if (info != null && info.responseData != null) { 357 responseMap.put(info.cert, 358 info.responseData.ocspBytes); 359 } else if (SSLLogger.isOn && 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"); 367 } 368 } 369 } 370 } catch (InterruptedException | ExecutionException exc) { 371 // Not sure what else to do here 372 if (SSLLogger.isOn && SSLLogger.isOn("respmgr")) { 373 SSLLogger.fine("Exception when getting data: ", exc); 374 } 375 } 376 } 377 378 return Collections.unmodifiableMap(responseMap); 379 } 380 381 /** 382 * Check the cache for a given {@code CertId}. 383 * 384 * @param cid the CertId of the response to look up 385 * @param ocspRequest the OCSP request structure sent by the client 386 * in the TLS status_request[_v2] hello extension. 387 * 388 * @return the {@code ResponseCacheEntry} for a specific CertId, or 389 * {@code null} if it is not found or a nonce extension has been 390 * requested by the caller. 391 */ 392 private ResponseCacheEntry getFromCache(CertId cid, 393 OCSPStatusRequest ocspRequest) { 394 // Determine if the nonce extension is present in the request. If 395 // so, then do not attempt to retrieve the response from the cache. 396 for (Extension ext : ocspRequest.extensions) { 397 if (ext.getId().equals( 398 PKIXExtensions.OCSPNonce_Id.toString())) { 399 if (SSLLogger.isOn && SSLLogger.isOn("respmgr")) { 400 SSLLogger.fine( 401 "Nonce extension found, skipping cache check"); 402 } 403 return null; 404 } 405 } 406 407 ResponseCacheEntry respEntry = responseCache.get(cid); 408 409 // If the response entry has a nextUpdate and it has expired 410 // before the cache expiration, purge it from the cache 411 // and do not return it as a cache hit. 412 if (respEntry != null && respEntry.nextUpdate != null && 413 respEntry.nextUpdate.before(new Date())) { 414 if (SSLLogger.isOn && SSLLogger.isOn("respmgr")) { 415 SSLLogger.fine( 416 "nextUpdate threshold exceeded, purging from cache"); 417 } 418 respEntry = null; 419 } 420 421 if (SSLLogger.isOn && SSLLogger.isOn("respmgr")) { 422 SSLLogger.fine( 423 "Check cache for SN" + cid.getSerialNumber() + ": " + 424 (respEntry != null ? "HIT" : "MISS")); 425 } 426 return respEntry; 427 } 428 429 @Override 430 public String toString() { 431 StringBuilder sb = new StringBuilder("StatusResponseManager: "); 432 433 sb.append("Core threads: ").append(threadMgr.getCorePoolSize()); 434 sb.append(", Cache timeout: "); 435 if (cacheLifetime > 0) { 436 sb.append(cacheLifetime).append(" seconds"); 437 } else { 438 sb.append(" indefinite"); 439 } 440 441 sb.append(", Cache MaxSize: "); 442 if (cacheCapacity > 0) { 443 sb.append(cacheCapacity).append(" items"); 444 } else { 445 sb.append(" unbounded"); 446 } 447 448 sb.append(", Default URI: "); 449 if (defaultResponder != null) { 450 sb.append(defaultResponder); 451 } else { 452 sb.append("NONE"); 453 } 454 455 return sb.toString(); 456 } 457 458 /** 459 * Inner class used to group request and response data. 460 */ 461 class StatusInfo { 462 final X509Certificate cert; 463 final CertId cid; 464 final URI responder; 465 ResponseCacheEntry responseData; 466 467 /** 468 * Create a StatusInfo object from certificate data. 469 * 470 * @param subjectCert the certificate to be checked for revocation 471 * @param issuerCert the issuer of the {@code subjectCert} 472 * 473 * @throws IOException if CertId creation from the certificate fails 474 */ 475 StatusInfo(X509Certificate subjectCert, X509Certificate issuerCert) 476 throws IOException { 477 this(subjectCert, new CertId(issuerCert, 478 new SerialNumber(subjectCert.getSerialNumber()))); 479 } 480 481 /** 482 * Create a StatusInfo object from an existing subject certificate 483 * and its corresponding CertId. 484 * 485 * @param subjectCert the certificate to be checked for revocation 486 * @param cid the CertId for {@code subjectCert} 487 */ 488 StatusInfo(X509Certificate subjectCert, CertId certId) { 489 cert = subjectCert; 490 cid = certId; 491 responder = getURI(cert); 492 responseData = null; 493 } 498 * responseData and extensions fields, which should not persist 499 * in a rescheduled fetch. 500 * 501 * @param orig the original {@code StatusInfo} 502 */ 503 StatusInfo(StatusInfo orig) { 504 this.cert = orig.cert; 505 this.cid = orig.cid; 506 this.responder = orig.responder; 507 this.responseData = null; 508 } 509 510 /** 511 * Return a String representation of the {@code StatusInfo} 512 * 513 * @return a {@code String} representation of this object 514 */ 515 @Override 516 public String toString() { 517 StringBuilder sb = new StringBuilder("StatusInfo:"); 518 sb.append("\n\tCert: ").append( 519 this.cert.getSubjectX500Principal()); 520 sb.append("\n\tSerial: ").append(this.cert.getSerialNumber()); 521 sb.append("\n\tResponder: ").append(this.responder); 522 sb.append("\n\tResponse data: ").append( 523 this.responseData != null ? 524 (this.responseData.ocspBytes.length + " bytes") : 525 "<NULL>"); 526 return sb.toString(); 527 } 528 } 529 530 /** 531 * Static nested class used as the data kept in the response cache. 532 */ 533 class ResponseCacheEntry { 534 final OCSPResponse.ResponseStatus status; 535 final byte[] ocspBytes; 536 final Date nextUpdate; 537 final OCSPResponse.SingleResponse singleResp; 538 final ResponderId respId; 539 540 /** 541 * Create a new cache entry from the raw bytes of the response 542 * 543 * @param responseBytes the DER encoding for the OCSP response 544 * 545 * @throws IOException if an {@code OCSPResponse} cannot be 546 * created from the encoded bytes. 547 */ 548 ResponseCacheEntry(byte[] responseBytes, CertId cid) 549 throws IOException { 550 Objects.requireNonNull(responseBytes, 551 "Non-null responseBytes required"); 552 Objects.requireNonNull(cid, "Non-null Cert ID required"); 553 554 ocspBytes = responseBytes.clone(); 555 OCSPResponse oResp = new OCSPResponse(ocspBytes); 556 status = oResp.getResponseStatus(); 557 respId = oResp.getResponderId(); 558 singleResp = oResp.getSingleResponse(cid); 559 if (status == OCSPResponse.ResponseStatus.SUCCESSFUL) { 560 if (singleResp != null) { 561 // Pull out the nextUpdate field in advance because the 562 // Date is cloned. 563 nextUpdate = singleResp.getNextUpdate(); 564 } else { 565 throw new IOException( 566 "Unable to find SingleResponse for SN " + 567 cid.getSerialNumber()); 568 } 569 } else { 570 nextUpdate = null; 571 } 572 } 573 } 574 575 /** 576 * Inner Callable class that does the actual work of looking up OCSP 577 * responses, first looking at the cache and doing OCSP requests if 578 * a cache miss occurs. 579 */ 580 class OCSPFetchCall implements Callable<StatusInfo> { 581 StatusInfo statInfo; 582 OCSPStatusRequest ocspRequest; 583 List<Extension> extensions; 584 List<ResponderId> responderIds; 585 586 /** 587 * A constructor that builds the OCSPFetchCall from the provided 588 * StatusInfo and information from the status_request[_v2] 589 * extension. 590 * 591 * @param info the {@code StatusInfo} containing the subject 592 * certificate, CertId, and other supplemental info. 593 * @param request the {@code OCSPStatusRequest} containing any 594 * responder IDs and extensions. 595 */ 596 public OCSPFetchCall(StatusInfo info, OCSPStatusRequest request) { 597 statInfo = Objects.requireNonNull(info, 598 "Null StatusInfo not allowed"); 599 ocspRequest = Objects.requireNonNull(request, 600 "Null OCSPStatusRequest not allowed"); 601 extensions = ocspRequest.extensions; 602 responderIds = ocspRequest.responderIds; 603 } 604 605 /** 606 * Get an OCSP response, either from the cache or from a responder. 607 * 608 * @return The StatusInfo object passed into the 609 * {@code OCSPFetchCall} constructor, with the 610 * {@code responseData} field filled in with the response 611 * or {@code null} if no response can be obtained. 612 */ 613 @Override 614 public StatusInfo call() { 615 if (SSLLogger.isOn && SSLLogger.isOn("respmgr")) { 616 SSLLogger.fine( 617 "Starting fetch for SN " + 618 statInfo.cid.getSerialNumber()); 619 } 620 try { 621 ResponseCacheEntry cacheEntry; 622 List<Extension> extsToSend; 623 624 if (statInfo.responder == null) { 625 // If we have no URI then there's nothing to do 626 // but return. 627 if (SSLLogger.isOn && SSLLogger.isOn("respmgr")) { 628 SSLLogger.fine( 629 "Null URI detected, OCSP fetch aborted"); 630 } 631 return statInfo; 632 } else { 633 if (SSLLogger.isOn && SSLLogger.isOn("respmgr")) { 634 SSLLogger.fine( 635 "Attempting fetch from " + statInfo.responder); 636 } 637 } 638 639 // If the StatusResponseManager has been configured to not 640 // forward extensions, then set extensions to an empty 641 // list. 642 // 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 // 649 // ResponderId selection is a feature that will be 650 // supported in the future. 651 extsToSend = (ignoreExtensions || !responderIds.isEmpty()) ? 652 Collections.emptyList() : extensions; 653 654 byte[] respBytes = OCSP.getOCSPBytes( 655 Collections.singletonList(statInfo.cid), 656 statInfo.responder, extsToSend); 657 658 if (respBytes != null) { 659 // Place the data into the response cache 660 cacheEntry = new ResponseCacheEntry(respBytes, 661 statInfo.cid); 662 663 // Get the response status and act on it appropriately 664 if (SSLLogger.isOn && SSLLogger.isOn("respmgr")) { 665 SSLLogger.fine("OCSP Status: " + cacheEntry.status + 666 " (" + respBytes.length + " bytes)"); 667 } 668 if (cacheEntry.status == 669 OCSPResponse.ResponseStatus.SUCCESSFUL) { 670 // Set the response in the returned StatusInfo 671 statInfo.responseData = cacheEntry; 672 673 // Add the response to the cache (if applicable) 674 addToCache(statInfo.cid, cacheEntry); 675 } 676 } else { 677 if (SSLLogger.isOn && SSLLogger.isOn("respmgr")) { 678 SSLLogger.fine( 679 "No data returned from OCSP Responder"); 680 } 681 } 682 } catch (IOException ioe) { 683 if (SSLLogger.isOn && SSLLogger.isOn("respmgr")) { 684 SSLLogger.fine("Caught exception: ", ioe); 685 } 686 } 687 688 return statInfo; 689 } 690 691 /** 692 * Add a response to the cache. 693 * 694 * @param certId The {@code CertId} for the OCSP response 695 * @param entry A cache entry containing the response bytes and 696 * the {@code OCSPResponse} built from those bytes. 697 */ 698 private void addToCache(CertId certId, ResponseCacheEntry entry) { 699 // If no cache lifetime has been set on entries then 700 // don't cache this response if there is no nextUpdate field 701 if (entry.nextUpdate == null && cacheLifetime == 0) { 702 if (SSLLogger.isOn && SSLLogger.isOn("respmgr")) { 703 SSLLogger.fine("Not caching this OCSP response"); 704 } 705 } else { 706 responseCache.put(certId, entry); 707 if (SSLLogger.isOn && SSLLogger.isOn("respmgr")) { 708 SSLLogger.fine( 709 "Added response for SN " + 710 certId.getSerialNumber() + 711 " to cache"); 712 } 713 } 714 } 715 716 /** 717 * Determine the delay to use when scheduling the task that will 718 * update the OCSP response. This is the shorter time between the 719 * cache lifetime and the nextUpdate. If no nextUpdate is present 720 * in the response, then only the cache lifetime is used. 721 * If cache timeouts are disabled (a zero value) and there's no 722 * nextUpdate, then the entry is not cached and no rescheduling 723 * will take place. 724 * 725 * @param nextUpdate a {@code Date} object corresponding to the 726 * next update time from a SingleResponse. 727 * 728 * @return the number of seconds of delay before the next fetch 729 * should be executed. A zero value means that the fetch 730 * should happen immediately, while a value less than zero 731 * indicates no rescheduling should be done. 732 */ 733 private long getNextTaskDelay(Date nextUpdate) { 734 long delaySec; 735 int lifetime = getCacheLifetime(); 736 737 if (nextUpdate != null) { 738 long nuDiffSec = (nextUpdate.getTime() - 739 System.currentTimeMillis()) / 1000; 740 delaySec = lifetime > 0 ? Long.min(nuDiffSec, lifetime) : 741 nuDiffSec; 742 } else { 743 delaySec = lifetime > 0 ? lifetime : -1; 744 } 745 746 return delaySec; 747 } 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 } 962 } 963 |