1 /* 2 * Copyright (c) 2015, 2020, 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.io.ByteArrayInputStream; 30 import java.nio.ByteBuffer; 31 import java.security.cert.Extension; 32 import java.security.cert.CertificateFactory; 33 import java.security.cert.CertificateException; 34 import java.security.cert.X509Certificate; 35 import java.text.MessageFormat; 36 import java.util.ArrayList; 37 import java.util.List; 38 import java.util.Locale; 39 import javax.net.ssl.SSLProtocolException; 40 import sun.security.provider.certpath.OCSPResponse; 41 import sun.security.provider.certpath.ResponderId; 42 import sun.security.ssl.SSLExtension.ExtensionConsumer; 43 import sun.security.ssl.SSLExtension.SSLExtensionSpec; 44 import sun.security.ssl.SSLHandshake.HandshakeMessage; 45 import sun.security.util.DerInputStream; 46 import sun.security.util.DerValue; 47 import sun.security.util.HexDumpEncoder; 48 49 /** 50 * Pack of "status_request" and "status_request_v2" extensions. 51 */ 52 final class CertStatusExtension { 53 static final HandshakeProducer chNetworkProducer = 54 new CHCertStatusReqProducer(); 55 static final ExtensionConsumer chOnLoadConsumer = 56 new CHCertStatusReqConsumer(); 57 58 static final HandshakeProducer shNetworkProducer = 59 new SHCertStatusReqProducer(); 60 static final ExtensionConsumer shOnLoadConsumer = 61 new SHCertStatusReqConsumer(); 62 63 static final HandshakeProducer ctNetworkProducer = 64 new CTCertStatusResponseProducer(); 65 static final ExtensionConsumer ctOnLoadConsumer = 66 new CTCertStatusResponseConsumer(); 67 68 static final SSLStringizer certStatusReqStringizer = 69 new CertStatusRequestStringizer(); 70 71 static final HandshakeProducer chV2NetworkProducer = 72 new CHCertStatusReqV2Producer(); 73 static final ExtensionConsumer chV2OnLoadConsumer = 74 new CHCertStatusReqV2Consumer(); 75 76 static final HandshakeProducer shV2NetworkProducer = 77 new SHCertStatusReqV2Producer(); 78 static final ExtensionConsumer shV2OnLoadConsumer = 79 new SHCertStatusReqV2Consumer(); 80 81 static final SSLStringizer certStatusReqV2Stringizer = 82 new CertStatusRequestsStringizer(); 83 84 static final SSLStringizer certStatusRespStringizer = 85 new CertStatusRespStringizer(); 86 87 /** 88 * The "status_request" extension. 89 * 90 * RFC6066 defines the TLS extension,"status_request" (type 0x5), 91 * which allows the client to request that the server perform OCSP 92 * on the client's behalf. 93 * 94 * The "extension data" field of this extension contains a 95 * "CertificateStatusRequest" structure: 96 * 97 * struct { 98 * CertificateStatusType status_type; 99 * select (status_type) { 100 * case ocsp: OCSPStatusRequest; 101 * } request; 102 * } CertificateStatusRequest; 103 * 104 * enum { ocsp(1), (255) } CertificateStatusType; 105 * 106 * struct { 107 * ResponderID responder_id_list<0..2^16-1>; 108 * Extensions request_extensions; 109 * } OCSPStatusRequest; 110 * 111 * opaque ResponderID<1..2^16-1>; 112 * opaque Extensions<0..2^16-1>; 113 */ 114 static final class CertStatusRequestSpec implements SSLExtensionSpec { 115 static final CertStatusRequestSpec DEFAULT = 116 new CertStatusRequestSpec(OCSPStatusRequest.EMPTY_OCSP); 117 118 final CertStatusRequest statusRequest; 119 CertStatusRequestSpec(CertStatusRequest statusRequest)120 private CertStatusRequestSpec(CertStatusRequest statusRequest) { 121 this.statusRequest = statusRequest; 122 } 123 CertStatusRequestSpec(ByteBuffer buffer)124 private CertStatusRequestSpec(ByteBuffer buffer) throws IOException { 125 // Is it a empty extension_data? 126 if (buffer.remaining() == 0) { 127 // server response 128 this.statusRequest = null; 129 return; 130 } 131 132 if (buffer.remaining() < 1) { 133 throw new SSLProtocolException( 134 "Invalid status_request extension: insufficient data"); 135 } 136 137 byte statusType = (byte)Record.getInt8(buffer); 138 byte[] encoded = new byte[buffer.remaining()]; 139 if (encoded.length != 0) { 140 buffer.get(encoded); 141 } 142 if (statusType == CertStatusRequestType.OCSP.id) { 143 this.statusRequest = new OCSPStatusRequest(statusType, encoded); 144 } else { 145 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { 146 SSLLogger.info( 147 "Unknown certificate status request " + 148 "(status type: " + statusType + ")"); 149 } 150 151 this.statusRequest = new CertStatusRequest(statusType, encoded); 152 } 153 } 154 155 @Override toString()156 public String toString() { 157 return statusRequest == null ? 158 "<empty>" : statusRequest.toString(); 159 } 160 } 161 162 /** 163 * Defines the CertificateStatus response structure as outlined in 164 * RFC 6066. This will contain a status response type, plus a single, 165 * non-empty OCSP response in DER-encoded form. 166 * 167 * struct { 168 * CertificateStatusType status_type; 169 * select (status_type) { 170 * case ocsp: OCSPResponse; 171 * } response; 172 * } CertificateStatus; 173 */ 174 static final class CertStatusResponseSpec implements SSLExtensionSpec { 175 final CertStatusResponse statusResponse; 176 CertStatusResponseSpec(CertStatusResponse resp)177 private CertStatusResponseSpec(CertStatusResponse resp) { 178 this.statusResponse = resp; 179 } 180 CertStatusResponseSpec(ByteBuffer buffer)181 private CertStatusResponseSpec(ByteBuffer buffer) throws IOException { 182 if (buffer.remaining() < 2) { 183 throw new SSLProtocolException( 184 "Invalid status_request extension: insufficient data"); 185 } 186 187 // Get the status type (1 byte) and response data (vector) 188 byte type = (byte)Record.getInt8(buffer); 189 byte[] respData = Record.getBytes24(buffer); 190 191 // Create the CertStatusResponse based on the type 192 if (type == CertStatusRequestType.OCSP.id) { 193 this.statusResponse = new OCSPStatusResponse(type, respData); 194 } else { 195 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { 196 SSLLogger.info( 197 "Unknown certificate status response " + 198 "(status type: " + type + ")"); 199 } 200 201 this.statusResponse = new CertStatusResponse(type, respData); 202 } 203 } 204 205 @Override toString()206 public String toString() { 207 return statusResponse == null ? 208 "<empty>" : statusResponse.toString(); 209 } 210 } 211 212 private static final 213 class CertStatusRequestStringizer implements SSLStringizer { 214 @Override toString(ByteBuffer buffer)215 public String toString(ByteBuffer buffer) { 216 try { 217 return (new CertStatusRequestSpec(buffer)).toString(); 218 } catch (IOException ioe) { 219 // For debug logging only, so please swallow exceptions. 220 return ioe.getMessage(); 221 } 222 } 223 } 224 225 private static final 226 class CertStatusRespStringizer implements SSLStringizer { 227 @Override toString(ByteBuffer buffer)228 public String toString(ByteBuffer buffer) { 229 try { 230 return (new CertStatusResponseSpec(buffer)).toString(); 231 } catch (IOException ioe) { 232 // For debug logging only, so please swallow exceptions. 233 return ioe.getMessage(); 234 } 235 } 236 } 237 238 static enum CertStatusRequestType { 239 OCSP ((byte)0x01, "ocsp"), // RFC 6066/6961 240 OCSP_MULTI ((byte)0x02, "ocsp_multi"); // RFC 6961 241 242 final byte id; 243 final String name; 244 CertStatusRequestType(byte id, String name)245 private CertStatusRequestType(byte id, String name) { 246 this.id = id; 247 this.name = name; 248 } 249 250 /** 251 * Returns the enum constant of the specified id (see RFC 6066). 252 */ valueOf(byte id)253 static CertStatusRequestType valueOf(byte id) { 254 for (CertStatusRequestType srt : CertStatusRequestType.values()) { 255 if (srt.id == id) { 256 return srt; 257 } 258 } 259 260 return null; 261 } 262 nameOf(byte id)263 static String nameOf(byte id) { 264 for (CertStatusRequestType srt : CertStatusRequestType.values()) { 265 if (srt.id == id) { 266 return srt.name; 267 } 268 } 269 270 return "UNDEFINED-CERT-STATUS-TYPE(" + id + ")"; 271 } 272 } 273 274 static class CertStatusRequest { 275 final byte statusType; 276 final byte[] encodedRequest; 277 CertStatusRequest(byte statusType, byte[] encodedRequest)278 protected CertStatusRequest(byte statusType, byte[] encodedRequest) { 279 this.statusType = statusType; 280 this.encodedRequest = encodedRequest; 281 } 282 283 @Override toString()284 public String toString() { 285 MessageFormat messageFormat = new MessageFormat( 286 "\"certificate status type\": {0}\n" + 287 "\"encoded certificate status\": '{'\n" + 288 "{1}\n" + 289 "'}'", 290 Locale.ENGLISH); 291 292 HexDumpEncoder hexEncoder = new HexDumpEncoder(); 293 String encoded = hexEncoder.encodeBuffer(encodedRequest); 294 295 Object[] messageFields = { 296 CertStatusRequestType.nameOf(statusType), 297 Utilities.indent(encoded) 298 }; 299 300 return messageFormat.format(messageFields); 301 } 302 } 303 304 /* 305 * RFC6066 defines the TLS extension,"status_request" (type 0x5), 306 * which allows the client to request that the server perform OCSP 307 * on the client's behalf. 308 * 309 * The RFC defines an OCSPStatusRequest structure: 310 * 311 * struct { 312 * ResponderID responder_id_list<0..2^16-1>; 313 * Extensions request_extensions; 314 * } OCSPStatusRequest; 315 */ 316 static final class OCSPStatusRequest extends CertStatusRequest { 317 static final OCSPStatusRequest EMPTY_OCSP; 318 static final OCSPStatusRequest EMPTY_OCSP_MULTI; 319 320 final List<ResponderId> responderIds; 321 final List<Extension> extensions; 322 private final int ridListLen; 323 private final int extListLen; 324 325 static { 326 OCSPStatusRequest ocspReq = null; 327 OCSPStatusRequest multiReq = null; 328 329 try { 330 ocspReq = new OCSPStatusRequest( 331 CertStatusRequestType.OCSP.id, 332 new byte[] {0x00, 0x00, 0x00, 0x00}); 333 multiReq = new OCSPStatusRequest( 334 CertStatusRequestType.OCSP_MULTI.id, 335 new byte[] {0x00, 0x00, 0x00, 0x00}); 336 } catch (IOException ioe) { 337 // unlikely 338 } 339 340 EMPTY_OCSP = ocspReq; 341 EMPTY_OCSP_MULTI = multiReq; 342 } 343 OCSPStatusRequest(byte statusType, byte[] encoded)344 private OCSPStatusRequest(byte statusType, 345 byte[] encoded) throws IOException { 346 super(statusType, encoded); 347 348 if (encoded == null || encoded.length < 4) { 349 // 2: length of responder_id_list 350 // +2: length of request_extensions 351 throw new SSLProtocolException( 352 "Invalid OCSP status request: insufficient data"); 353 } 354 355 List<ResponderId> rids = new ArrayList<>(); 356 List<Extension> exts = new ArrayList<>(); 357 ByteBuffer m = ByteBuffer.wrap(encoded); 358 359 this.ridListLen = Record.getInt16(m); 360 if (m.remaining() < (ridListLen + 2)) { 361 throw new SSLProtocolException( 362 "Invalid OCSP status request: insufficient data"); 363 } 364 365 int ridListBytesRemaining = ridListLen; 366 while (ridListBytesRemaining >= 2) { // 2: length of responder_id 367 byte[] ridBytes = Record.getBytes16(m); 368 try { 369 rids.add(new ResponderId(ridBytes)); 370 } catch (IOException ioe) { 371 throw new SSLProtocolException( 372 "Invalid OCSP status request: invalid responder ID"); 373 } 374 ridListBytesRemaining -= ridBytes.length + 2; 375 } 376 377 if (ridListBytesRemaining != 0) { 378 throw new SSLProtocolException( 379 "Invalid OCSP status request: incomplete data"); 380 } 381 382 byte[] extListBytes = Record.getBytes16(m); 383 this.extListLen = extListBytes.length; 384 if (extListLen > 0) { 385 try { 386 DerInputStream dis = new DerInputStream(extListBytes); 387 DerValue[] extSeqContents = 388 dis.getSequence(extListBytes.length); 389 for (DerValue extDerVal : extSeqContents) { 390 exts.add(new sun.security.x509.Extension(extDerVal)); 391 } 392 } catch (IOException ioe) { 393 throw new SSLProtocolException( 394 "Invalid OCSP status request: invalid extension"); 395 } 396 } 397 398 this.responderIds = rids; 399 this.extensions = exts; 400 } 401 402 @Override toString()403 public String toString() { 404 MessageFormat messageFormat = new MessageFormat( 405 "\"certificate status type\": {0}\n" + 406 "\"OCSP status request\": '{'\n" + 407 "{1}\n" + 408 "'}'", 409 Locale.ENGLISH); 410 411 MessageFormat requestFormat = new MessageFormat( 412 "\"responder_id\": {0}\n" + 413 "\"request extensions\": '{'\n" + 414 "{1}\n" + 415 "'}'", 416 Locale.ENGLISH); 417 418 String ridStr = "<empty>"; 419 if (!responderIds.isEmpty()) { 420 ridStr = responderIds.toString(); 421 } 422 423 String extsStr = "<empty>"; 424 if (!extensions.isEmpty()) { 425 StringBuilder extBuilder = new StringBuilder(512); 426 boolean isFirst = true; 427 for (Extension ext : this.extensions) { 428 if (isFirst) { 429 isFirst = false; 430 } else { 431 extBuilder.append(",\n"); 432 } 433 extBuilder.append("{\n"). 434 append(Utilities.indent(ext.toString())). 435 append("}"); 436 } 437 438 extsStr = extBuilder.toString(); 439 } 440 441 Object[] requestFields = { 442 ridStr, 443 Utilities.indent(extsStr) 444 }; 445 String ocspStatusRequest = requestFormat.format(requestFields); 446 447 Object[] messageFields = { 448 CertStatusRequestType.nameOf(statusType), 449 Utilities.indent(ocspStatusRequest) 450 }; 451 452 return messageFormat.format(messageFields); 453 } 454 } 455 456 static class CertStatusResponse { 457 final byte statusType; 458 final byte[] encodedResponse; 459 CertStatusResponse(byte statusType, byte[] respDer)460 protected CertStatusResponse(byte statusType, byte[] respDer) { 461 this.statusType = statusType; 462 this.encodedResponse = respDer; 463 } 464 toByteArray()465 byte[] toByteArray() throws IOException { 466 // Create a byte array large enough to handle the status_type 467 // field (1) + OCSP length (3) + OCSP data (variable) 468 byte[] outData = new byte[encodedResponse.length + 4]; 469 ByteBuffer buf = ByteBuffer.wrap(outData); 470 Record.putInt8(buf, statusType); 471 Record.putBytes24(buf, encodedResponse); 472 return buf.array(); 473 } 474 475 @Override toString()476 public String toString() { 477 MessageFormat messageFormat = new MessageFormat( 478 "\"certificate status response type\": {0}\n" + 479 "\"encoded certificate status\": '{'\n" + 480 "{1}\n" + 481 "'}'", 482 Locale.ENGLISH); 483 484 HexDumpEncoder hexEncoder = new HexDumpEncoder(); 485 String encoded = hexEncoder.encodeBuffer(encodedResponse); 486 487 Object[] messageFields = { 488 CertStatusRequestType.nameOf(statusType), 489 Utilities.indent(encoded) 490 }; 491 492 return messageFormat.format(messageFields); 493 } 494 } 495 496 static final class OCSPStatusResponse extends CertStatusResponse { 497 final OCSPResponse ocspResponse; 498 OCSPStatusResponse(byte statusType, byte[] encoded)499 private OCSPStatusResponse(byte statusType, 500 byte[] encoded) throws IOException { 501 super(statusType, encoded); 502 503 // The DER-encoded OCSP response must not be zero length 504 if (encoded == null || encoded.length < 1) { 505 throw new SSLProtocolException( 506 "Invalid OCSP status response: insufficient data"); 507 } 508 509 // Otherwise, make an OCSPResponse object from the data 510 ocspResponse = new OCSPResponse(encoded); 511 } 512 513 @Override toString()514 public String toString() { 515 MessageFormat messageFormat = new MessageFormat( 516 "\"certificate status response type\": {0}\n" + 517 "\"OCSP status response\": '{'\n" + 518 "{1}\n" + 519 "'}'", 520 Locale.ENGLISH); 521 522 Object[] messageFields = { 523 CertStatusRequestType.nameOf(statusType), 524 Utilities.indent(ocspResponse.toString()) 525 }; 526 527 return messageFormat.format(messageFields); 528 } 529 } 530 531 /** 532 * Network data producer of a "status_request" extension in the 533 * ClientHello handshake message. 534 */ 535 private static final 536 class CHCertStatusReqProducer implements HandshakeProducer { 537 // Prevent instantiation of this class. CHCertStatusReqProducer()538 private CHCertStatusReqProducer() { 539 // blank 540 } 541 542 @Override produce(ConnectionContext context, HandshakeMessage message)543 public byte[] produce(ConnectionContext context, 544 HandshakeMessage message) throws IOException { 545 // The producing happens in client side only. 546 ClientHandshakeContext chc = (ClientHandshakeContext)context; 547 548 if (!chc.sslContext.isStaplingEnabled(true)) { 549 return null; 550 } 551 552 if (!chc.sslConfig.isAvailable(SSLExtension.CH_STATUS_REQUEST)) { 553 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { 554 SSLLogger.fine( 555 "Ignore unavailable extension: " + 556 SSLExtension.CH_STATUS_REQUEST.name); 557 } 558 return null; 559 } 560 561 // Produce the extension. 562 // 563 // We are using empty OCSPStatusRequest at present. May extend to 564 // support specific responder or extensions later. 565 byte[] extData = new byte[] {0x01, 0x00, 0x00, 0x00, 0x00}; 566 567 // Update the context. 568 chc.handshakeExtensions.put(SSLExtension.CH_STATUS_REQUEST, 569 CertStatusRequestSpec.DEFAULT); 570 571 return extData; 572 } 573 } 574 575 /** 576 * Network data consumer of a "status_request" extension in the 577 * ClientHello handshake message. 578 */ 579 private static final 580 class CHCertStatusReqConsumer implements ExtensionConsumer { 581 // Prevent instantiation of this class. CHCertStatusReqConsumer()582 private CHCertStatusReqConsumer() { 583 // blank 584 } 585 586 @Override consume(ConnectionContext context, HandshakeMessage message, ByteBuffer buffer)587 public void consume(ConnectionContext context, 588 HandshakeMessage message, ByteBuffer buffer) throws IOException { 589 590 // The consuming happens in server side only. 591 ServerHandshakeContext shc = (ServerHandshakeContext)context; 592 593 if (!shc.sslConfig.isAvailable(SSLExtension.CH_STATUS_REQUEST)) { 594 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { 595 SSLLogger.fine("Ignore unavailable extension: " + 596 SSLExtension.CH_STATUS_REQUEST.name); 597 } 598 return; // ignore the extension 599 } 600 601 // Parse the extension. 602 CertStatusRequestSpec spec; 603 try { 604 spec = new CertStatusRequestSpec(buffer); 605 } catch (IOException ioe) { 606 throw shc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, ioe); 607 } 608 609 // Update the context. 610 shc.handshakeExtensions.put(SSLExtension.CH_STATUS_REQUEST, spec); 611 if (!shc.isResumption && 612 !shc.negotiatedProtocol.useTLS13PlusSpec()) { 613 shc.handshakeProducers.put(SSLHandshake.CERTIFICATE_STATUS.id, 614 SSLHandshake.CERTIFICATE_STATUS); 615 } // Otherwise, the certificate status presents in server cert. 616 617 // No impact on session resumption. 618 } 619 } 620 621 /** 622 * Network data producer of a "status_request" extension in the 623 * ServerHello handshake message. 624 */ 625 private static final 626 class SHCertStatusReqProducer implements HandshakeProducer { 627 // Prevent instantiation of this class. SHCertStatusReqProducer()628 private SHCertStatusReqProducer() { 629 // blank 630 } 631 632 @Override produce(ConnectionContext context, HandshakeMessage message)633 public byte[] produce(ConnectionContext context, 634 HandshakeMessage message) throws IOException { 635 // The producing happens in client side only. 636 ServerHandshakeContext shc = (ServerHandshakeContext)context; 637 638 // The StaplingParameters in the ServerHandshakeContext will 639 // contain the info about what kind of stapling (if any) to 640 // perform and whether this status_request extension should be 641 // produced or the status_request_v2 (found in a different producer) 642 // No explicit check is required for isStaplingEnabled here. If 643 // it is false then stapleParams will be null. If it is true 644 // then stapleParams may or may not be false and the check below 645 // is sufficient. 646 if ((shc.stapleParams == null) || 647 (shc.stapleParams.statusRespExt != 648 SSLExtension.CH_STATUS_REQUEST)) { 649 return null; // Do not produce status_request in ServerHello 650 } 651 652 // In response to "status_request" extension request only. 653 CertStatusRequestSpec spec = (CertStatusRequestSpec) 654 shc.handshakeExtensions.get(SSLExtension.CH_STATUS_REQUEST); 655 if (spec == null) { 656 // Ignore, no status_request extension requested. 657 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { 658 SSLLogger.finest("Ignore unavailable extension: " + 659 SSLExtension.CH_STATUS_REQUEST.name); 660 } 661 662 return null; // ignore the extension 663 } 664 665 // Is it a session resuming? 666 if (shc.isResumption) { 667 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { 668 SSLLogger.finest( 669 "No status_request response for session resuming"); 670 } 671 672 return null; // ignore the extension 673 } 674 675 // The "extension_data" in the extended ServerHello handshake 676 // message MUST be empty. 677 byte[] extData = new byte[0]; 678 679 // Update the context. 680 shc.handshakeExtensions.put(SSLExtension.SH_STATUS_REQUEST, 681 CertStatusRequestSpec.DEFAULT); 682 683 return extData; 684 } 685 } 686 687 /** 688 * Network data consumer of a "status_request" extension in the 689 * ServerHello handshake message. 690 */ 691 private static final 692 class SHCertStatusReqConsumer implements ExtensionConsumer { 693 // Prevent instantiation of this class. SHCertStatusReqConsumer()694 private SHCertStatusReqConsumer() { 695 // blank 696 } 697 698 @Override consume(ConnectionContext context, HandshakeMessage message, ByteBuffer buffer)699 public void consume(ConnectionContext context, 700 HandshakeMessage message, ByteBuffer buffer) throws IOException { 701 702 // The producing happens in client side only. 703 ClientHandshakeContext chc = (ClientHandshakeContext)context; 704 705 // In response to "status_request" extension request only. 706 CertStatusRequestSpec requestedCsr = (CertStatusRequestSpec) 707 chc.handshakeExtensions.get(SSLExtension.CH_STATUS_REQUEST); 708 if (requestedCsr == null) { 709 throw chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, 710 "Unexpected status_request extension in ServerHello"); 711 } 712 713 // Parse the extension. 714 if (buffer.hasRemaining()) { 715 throw chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, 716 "Invalid status_request extension in ServerHello message: " + 717 "the extension data must be empty"); 718 } 719 720 // Update the context. 721 chc.handshakeExtensions.put(SSLExtension.SH_STATUS_REQUEST, 722 CertStatusRequestSpec.DEFAULT); 723 724 // Since we've received a legitimate status_request in the 725 // ServerHello, stapling is active if it's been enabled. 726 chc.staplingActive = chc.sslContext.isStaplingEnabled(true); 727 if (chc.staplingActive) { 728 chc.handshakeConsumers.put(SSLHandshake.CERTIFICATE_STATUS.id, 729 SSLHandshake.CERTIFICATE_STATUS); 730 } 731 732 // No impact on session resumption. 733 } 734 } 735 736 /** 737 * The "status_request_v2" extension. 738 * 739 * RFC6961 defines the TLS extension,"status_request_v2" (type 0x5), 740 * which allows the client to request that the server perform OCSP 741 * on the client's behalf. 742 * 743 * The RFC defines an CertStatusReqItemV2 structure: 744 * 745 * struct { 746 * CertificateStatusType status_type; 747 * uint16 request_length; 748 * select (status_type) { 749 * case ocsp: OCSPStatusRequest; 750 * case ocsp_multi: OCSPStatusRequest; 751 * } request; 752 * } CertificateStatusRequestItemV2; 753 * 754 * enum { ocsp(1), ocsp_multi(2), (255) } CertificateStatusType; 755 * struct { 756 * ResponderID responder_id_list<0..2^16-1>; 757 * Extensions request_extensions; 758 * } OCSPStatusRequest; 759 * 760 * opaque ResponderID<1..2^16-1>; 761 * opaque Extensions<0..2^16-1>; 762 * 763 * struct { 764 * CertificateStatusRequestItemV2 765 * certificate_status_req_list<1..2^16-1>; 766 * } CertificateStatusRequestListV2; 767 */ 768 static final class CertStatusRequestV2Spec implements SSLExtensionSpec { 769 static final CertStatusRequestV2Spec DEFAULT = 770 new CertStatusRequestV2Spec(new CertStatusRequest[] { 771 OCSPStatusRequest.EMPTY_OCSP_MULTI}); 772 773 final CertStatusRequest[] certStatusRequests; 774 CertStatusRequestV2Spec(CertStatusRequest[] certStatusRequests)775 private CertStatusRequestV2Spec(CertStatusRequest[] certStatusRequests) { 776 this.certStatusRequests = certStatusRequests; 777 } 778 CertStatusRequestV2Spec(ByteBuffer message)779 private CertStatusRequestV2Spec(ByteBuffer message) throws IOException { 780 // Is it a empty extension_data? 781 if (message.remaining() == 0) { 782 // server response 783 this.certStatusRequests = new CertStatusRequest[0]; 784 return; 785 } 786 787 if (message.remaining() < 5) { // 2: certificate_status_req_list 788 // +1: status_type 789 // +2: request_length 790 throw new SSLProtocolException( 791 "Invalid status_request_v2 extension: insufficient data"); 792 } 793 794 int listLen = Record.getInt16(message); 795 if (listLen <= 0) { 796 throw new SSLProtocolException( 797 "certificate_status_req_list length must be positive " + 798 "(received length: " + listLen + ")"); 799 } 800 801 int remaining = listLen; 802 List<CertStatusRequest> statusRequests = new ArrayList<>(); 803 while (remaining > 0) { 804 byte statusType = (byte)Record.getInt8(message); 805 int requestLen = Record.getInt16(message); 806 807 if (message.remaining() < requestLen) { 808 throw new SSLProtocolException( 809 "Invalid status_request_v2 extension: " + 810 "insufficient data (request_length=" + requestLen + 811 ", remining=" + message.remaining() + ")"); 812 } 813 814 byte[] encoded = new byte[requestLen]; 815 if (encoded.length != 0) { 816 message.get(encoded); 817 } 818 remaining -= 3; // 1(status type) + 2(request_length) bytes 819 remaining -= requestLen; 820 821 if (statusType == CertStatusRequestType.OCSP.id || 822 statusType == CertStatusRequestType.OCSP_MULTI.id) { 823 if (encoded.length < 4) { 824 // 2: length of responder_id_list 825 // +2: length of request_extensions 826 throw new SSLProtocolException( 827 "Invalid status_request_v2 extension: " + 828 "insufficient data"); 829 } 830 statusRequests.add( 831 new OCSPStatusRequest(statusType, encoded)); 832 } else { 833 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { 834 SSLLogger.info( 835 "Unknown certificate status request " + 836 "(status type: " + statusType + ")"); 837 } 838 statusRequests.add( 839 new CertStatusRequest(statusType, encoded)); 840 } 841 } 842 843 certStatusRequests = 844 statusRequests.toArray(new CertStatusRequest[0]); 845 } 846 847 @Override toString()848 public String toString() { 849 if (certStatusRequests == null || certStatusRequests.length == 0) { 850 return "<empty>"; 851 } else { 852 MessageFormat messageFormat = new MessageFormat( 853 "\"cert status request\": '{'\n{0}\n'}'", Locale.ENGLISH); 854 855 StringBuilder builder = new StringBuilder(512); 856 boolean isFirst = true; 857 for (CertStatusRequest csr : certStatusRequests) { 858 if (isFirst) { 859 isFirst = false; 860 } else { 861 builder.append(", "); 862 } 863 Object[] messageFields = { 864 Utilities.indent(csr.toString()) 865 }; 866 builder.append(messageFormat.format(messageFields)); 867 } 868 869 return builder.toString(); 870 } 871 } 872 } 873 874 private static final 875 class CertStatusRequestsStringizer implements SSLStringizer { 876 @Override toString(ByteBuffer buffer)877 public String toString(ByteBuffer buffer) { 878 try { 879 return (new CertStatusRequestV2Spec(buffer)).toString(); 880 } catch (IOException ioe) { 881 // For debug logging only, so please swallow exceptions. 882 return ioe.getMessage(); 883 } 884 } 885 } 886 887 /** 888 * Network data producer of a "status_request_v2" extension in the 889 * ClientHello handshake message. 890 */ 891 private static final 892 class CHCertStatusReqV2Producer implements HandshakeProducer { 893 // Prevent instantiation of this class. CHCertStatusReqV2Producer()894 private CHCertStatusReqV2Producer() { 895 // blank 896 } 897 898 @Override produce(ConnectionContext context, HandshakeMessage message)899 public byte[] produce(ConnectionContext context, 900 HandshakeMessage message) throws IOException { 901 // The producing happens in client side only. 902 ClientHandshakeContext chc = (ClientHandshakeContext)context; 903 904 if (!chc.sslContext.isStaplingEnabled(true)) { 905 return null; 906 } 907 908 if (!chc.sslConfig.isAvailable(SSLExtension.CH_STATUS_REQUEST_V2)) { 909 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { 910 SSLLogger.finest( 911 "Ignore unavailable status_request_v2 extension"); 912 } 913 914 return null; 915 } 916 917 // Produce the extension. 918 // 919 // We are using empty OCSPStatusRequest at present. May extend to 920 // support specific responder or extensions later. 921 byte[] extData = new byte[] { 922 0x00, 0x07, 0x02, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00}; 923 924 // Update the context. 925 chc.handshakeExtensions.put(SSLExtension.CH_STATUS_REQUEST_V2, 926 CertStatusRequestV2Spec.DEFAULT); 927 928 return extData; 929 } 930 } 931 932 /** 933 * Network data consumer of a "status_request_v2" extension in the 934 * ClientHello handshake message. 935 */ 936 private static final 937 class CHCertStatusReqV2Consumer implements ExtensionConsumer { 938 // Prevent instantiation of this class. CHCertStatusReqV2Consumer()939 private CHCertStatusReqV2Consumer() { 940 // blank 941 } 942 943 @Override consume(ConnectionContext context, HandshakeMessage message, ByteBuffer buffer)944 public void consume(ConnectionContext context, 945 HandshakeMessage message, ByteBuffer buffer) throws IOException { 946 947 // The consuming happens in server side only. 948 ServerHandshakeContext shc = (ServerHandshakeContext)context; 949 950 if (!shc.sslConfig.isAvailable(SSLExtension.CH_STATUS_REQUEST_V2)) { 951 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { 952 SSLLogger.finest( 953 "Ignore unavailable status_request_v2 extension"); 954 } 955 956 return; // ignore the extension 957 } 958 959 // Parse the extension. 960 CertStatusRequestV2Spec spec; 961 try { 962 spec = new CertStatusRequestV2Spec(buffer); 963 } catch (IOException ioe) { 964 throw shc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, ioe); 965 } 966 967 // Update the context. 968 shc.handshakeExtensions.put(SSLExtension.CH_STATUS_REQUEST_V2, 969 spec); 970 if (!shc.isResumption) { 971 shc.handshakeProducers.putIfAbsent( 972 SSLHandshake.CERTIFICATE_STATUS.id, 973 SSLHandshake.CERTIFICATE_STATUS); 974 } 975 976 // No impact on session resumption. 977 } 978 } 979 980 /** 981 * Network data producer of a "status_request_v2" extension in the 982 * ServerHello handshake message. 983 */ 984 private static final 985 class SHCertStatusReqV2Producer implements HandshakeProducer { 986 // Prevent instantiation of this class. SHCertStatusReqV2Producer()987 private SHCertStatusReqV2Producer() { 988 // blank 989 } 990 991 @Override produce(ConnectionContext context, HandshakeMessage message)992 public byte[] produce(ConnectionContext context, 993 HandshakeMessage message) throws IOException { 994 // The producing happens in client side only. 995 996 ServerHandshakeContext shc = (ServerHandshakeContext)context; 997 // The StaplingParameters in the ServerHandshakeContext will 998 // contain the info about what kind of stapling (if any) to 999 // perform and whether this status_request extension should be 1000 // produced or the status_request_v2 (found in a different producer) 1001 // No explicit check is required for isStaplingEnabled here. If 1002 // it is false then stapleParams will be null. If it is true 1003 // then stapleParams may or may not be false and the check below 1004 // is sufficient. 1005 if ((shc.stapleParams == null) || 1006 (shc.stapleParams.statusRespExt != 1007 SSLExtension.CH_STATUS_REQUEST_V2)) { 1008 return null; // Do not produce status_request_v2 in SH 1009 } 1010 1011 // In response to "status_request_v2" extension request only 1012 CertStatusRequestV2Spec spec = (CertStatusRequestV2Spec) 1013 shc.handshakeExtensions.get(SSLExtension.CH_STATUS_REQUEST_V2); 1014 if (spec == null) { 1015 // Ignore, no status_request_v2 extension requested. 1016 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { 1017 SSLLogger.finest( 1018 "Ignore unavailable status_request_v2 extension"); 1019 } 1020 1021 return null; // ignore the extension 1022 } 1023 1024 // Is it a session resuming? 1025 if (shc.isResumption) { 1026 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { 1027 SSLLogger.finest( 1028 "No status_request_v2 response for session resumption"); 1029 } 1030 return null; // ignore the extension 1031 } 1032 1033 // The "extension_data" in the extended ServerHello handshake 1034 // message MUST be empty. 1035 byte[] extData = new byte[0]; 1036 1037 // Update the context. 1038 shc.handshakeExtensions.put(SSLExtension.SH_STATUS_REQUEST_V2, 1039 CertStatusRequestV2Spec.DEFAULT); 1040 1041 return extData; 1042 } 1043 } 1044 1045 /** 1046 * Network data consumer of a "status_request_v2" extension in the 1047 * ServerHello handshake message. 1048 */ 1049 private static final 1050 class SHCertStatusReqV2Consumer implements ExtensionConsumer { 1051 // Prevent instantiation of this class. SHCertStatusReqV2Consumer()1052 private SHCertStatusReqV2Consumer() { 1053 // blank 1054 } 1055 1056 @Override consume(ConnectionContext context, HandshakeMessage message, ByteBuffer buffer)1057 public void consume(ConnectionContext context, 1058 HandshakeMessage message, ByteBuffer buffer) throws IOException { 1059 1060 // The consumption happens in client side only. 1061 ClientHandshakeContext chc = (ClientHandshakeContext)context; 1062 1063 // In response to "status_request" extension request only 1064 CertStatusRequestV2Spec requestedCsr = (CertStatusRequestV2Spec) 1065 chc.handshakeExtensions.get(SSLExtension.CH_STATUS_REQUEST_V2); 1066 if (requestedCsr == null) { 1067 throw chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, 1068 "Unexpected status_request_v2 extension in ServerHello"); 1069 } 1070 1071 // Parse the extension. 1072 if (buffer.hasRemaining()) { 1073 throw chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, 1074 "Invalid status_request_v2 extension in ServerHello: " + 1075 "the extension data must be empty"); 1076 } 1077 1078 // Update the context. 1079 chc.handshakeExtensions.put(SSLExtension.SH_STATUS_REQUEST_V2, 1080 CertStatusRequestV2Spec.DEFAULT); 1081 1082 // Since we've received a legitimate status_request in the 1083 // ServerHello, stapling is active if it's been enabled. If it 1084 // is active, make sure we add the CertificateStatus message 1085 // consumer. 1086 chc.staplingActive = chc.sslContext.isStaplingEnabled(true); 1087 if (chc.staplingActive) { 1088 chc.handshakeConsumers.put(SSLHandshake.CERTIFICATE_STATUS.id, 1089 SSLHandshake.CERTIFICATE_STATUS); 1090 } 1091 1092 // No impact on session resumption. 1093 } 1094 } 1095 1096 private static final 1097 class CTCertStatusResponseProducer implements HandshakeProducer { 1098 // Prevent instantiation of this class. CTCertStatusResponseProducer()1099 private CTCertStatusResponseProducer() { 1100 // blank 1101 } 1102 1103 @Override produce(ConnectionContext context, HandshakeMessage message)1104 public byte[] produce(ConnectionContext context, 1105 HandshakeMessage message) throws IOException { 1106 ServerHandshakeContext shc = (ServerHandshakeContext)context; 1107 byte[] producedData = null; 1108 1109 // Stapling needs to be active and have valid data to proceed 1110 if (shc.stapleParams == null) { 1111 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { 1112 SSLLogger.finest( 1113 "Stapling is disabled for this connection"); 1114 } 1115 return null; 1116 } 1117 1118 // There needs to be a non-null CertificateEntry to proceed 1119 if (shc.currentCertEntry == null) { 1120 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { 1121 SSLLogger.finest("Found null CertificateEntry in context"); 1122 } 1123 return null; 1124 } 1125 1126 // Pull the certificate from the CertificateEntry and find 1127 // a response from the response map. If one exists we will 1128 // staple it. 1129 try { 1130 CertificateFactory cf = CertificateFactory.getInstance("X.509"); 1131 X509Certificate x509Cert = 1132 (X509Certificate)cf.generateCertificate( 1133 new ByteArrayInputStream( 1134 shc.currentCertEntry.encoded)); 1135 byte[] respBytes = shc.stapleParams.responseMap.get(x509Cert); 1136 if (respBytes == null) { 1137 // We're done with this entry. Clear it from the context 1138 if (SSLLogger.isOn && 1139 SSLLogger.isOn("ssl,handshake,verbose")) { 1140 SSLLogger.finest("No status response found for " + 1141 x509Cert.getSubjectX500Principal()); 1142 } 1143 shc.currentCertEntry = null; 1144 return null; 1145 } 1146 1147 // Build a proper response buffer from the stapling information 1148 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake,verbose")) { 1149 SSLLogger.finest("Found status response for " + 1150 x509Cert.getSubjectX500Principal() + 1151 ", response length: " + respBytes.length); 1152 } 1153 CertStatusResponse certResp = (shc.stapleParams.statReqType == 1154 CertStatusRequestType.OCSP) ? 1155 new OCSPStatusResponse(shc.stapleParams.statReqType.id, 1156 respBytes) : 1157 new CertStatusResponse(shc.stapleParams.statReqType.id, 1158 respBytes); 1159 producedData = certResp.toByteArray(); 1160 } catch (CertificateException ce) { 1161 throw shc.conContext.fatal(Alert.BAD_CERTIFICATE, 1162 "Failed to parse server certificates", ce); 1163 } catch (IOException ioe) { 1164 throw shc.conContext.fatal(Alert.BAD_CERT_STATUS_RESPONSE, 1165 "Failed to parse certificate status response", ioe); 1166 } 1167 1168 // Clear the pinned CertificateEntry from the context 1169 shc.currentCertEntry = null; 1170 return producedData; 1171 } 1172 } 1173 1174 private static final 1175 class CTCertStatusResponseConsumer implements ExtensionConsumer { 1176 // Prevent instantiation of this class. CTCertStatusResponseConsumer()1177 private CTCertStatusResponseConsumer() { 1178 // blank 1179 } 1180 1181 @Override consume(ConnectionContext context, HandshakeMessage message, ByteBuffer buffer)1182 public void consume(ConnectionContext context, 1183 HandshakeMessage message, ByteBuffer buffer) throws IOException { 1184 // The consumption happens in client side only. 1185 ClientHandshakeContext chc = (ClientHandshakeContext)context; 1186 1187 // Parse the extension. 1188 CertStatusResponseSpec spec; 1189 try { 1190 spec = new CertStatusResponseSpec(buffer); 1191 } catch (IOException ioe) { 1192 throw chc.conContext.fatal(Alert.DECODE_ERROR, ioe); 1193 } 1194 1195 if (chc.sslContext.isStaplingEnabled(true)) { 1196 // Activate stapling 1197 chc.staplingActive = true; 1198 } else { 1199 // Do no further processing of stapled responses 1200 return; 1201 } 1202 1203 // Get response list from the session. This is unmodifiable 1204 // so we need to create a new list. Then add this new response 1205 // to the end and submit it back to the session object. 1206 if ((chc.handshakeSession != null) && (!chc.isResumption)) { 1207 List<byte[]> respList = new ArrayList<>( 1208 chc.handshakeSession.getStatusResponses()); 1209 respList.add(spec.statusResponse.encodedResponse); 1210 chc.handshakeSession.setStatusResponses(respList); 1211 } else { 1212 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake,verbose")) { 1213 SSLLogger.finest( 1214 "Ignoring stapled data on resumed session"); 1215 } 1216 } 1217 } 1218 } 1219 } 1220