Path: blob/master/src/java.base/share/classes/sun/security/ssl/CertStatusExtension.java
41159 views
/*1* Copyright (c) 2015, 2020, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package sun.security.ssl;2627import java.io.IOException;28import java.io.ByteArrayInputStream;29import java.nio.ByteBuffer;30import java.security.cert.Extension;31import java.security.cert.CertificateFactory;32import java.security.cert.CertificateException;33import java.security.cert.X509Certificate;34import java.text.MessageFormat;35import java.util.ArrayList;36import java.util.List;37import java.util.Locale;38import javax.net.ssl.SSLProtocolException;39import sun.security.provider.certpath.OCSPResponse;40import sun.security.provider.certpath.ResponderId;41import sun.security.ssl.SSLExtension.ExtensionConsumer;42import sun.security.ssl.SSLExtension.SSLExtensionSpec;43import sun.security.ssl.SSLHandshake.HandshakeMessage;44import sun.security.util.DerInputStream;45import sun.security.util.DerValue;46import sun.security.util.HexDumpEncoder;4748/**49* Pack of "status_request" and "status_request_v2" extensions.50*/51final class CertStatusExtension {52static final HandshakeProducer chNetworkProducer =53new CHCertStatusReqProducer();54static final ExtensionConsumer chOnLoadConsumer =55new CHCertStatusReqConsumer();5657static final HandshakeProducer shNetworkProducer =58new SHCertStatusReqProducer();59static final ExtensionConsumer shOnLoadConsumer =60new SHCertStatusReqConsumer();6162static final HandshakeProducer ctNetworkProducer =63new CTCertStatusResponseProducer();64static final ExtensionConsumer ctOnLoadConsumer =65new CTCertStatusResponseConsumer();6667static final SSLStringizer certStatusReqStringizer =68new CertStatusRequestStringizer();6970static final HandshakeProducer chV2NetworkProducer =71new CHCertStatusReqV2Producer();72static final ExtensionConsumer chV2OnLoadConsumer =73new CHCertStatusReqV2Consumer();7475static final HandshakeProducer shV2NetworkProducer =76new SHCertStatusReqV2Producer();77static final ExtensionConsumer shV2OnLoadConsumer =78new SHCertStatusReqV2Consumer();7980static final SSLStringizer certStatusReqV2Stringizer =81new CertStatusRequestsStringizer();8283static final SSLStringizer certStatusRespStringizer =84new CertStatusRespStringizer();8586/**87* The "status_request" extension.88*89* RFC6066 defines the TLS extension,"status_request" (type 0x5),90* which allows the client to request that the server perform OCSP91* on the client's behalf.92*93* The "extension data" field of this extension contains a94* "CertificateStatusRequest" structure:95*96* struct {97* CertificateStatusType status_type;98* select (status_type) {99* case ocsp: OCSPStatusRequest;100* } request;101* } CertificateStatusRequest;102*103* enum { ocsp(1), (255) } CertificateStatusType;104*105* struct {106* ResponderID responder_id_list<0..2^16-1>;107* Extensions request_extensions;108* } OCSPStatusRequest;109*110* opaque ResponderID<1..2^16-1>;111* opaque Extensions<0..2^16-1>;112*/113static final class CertStatusRequestSpec implements SSLExtensionSpec {114static final CertStatusRequestSpec DEFAULT =115new CertStatusRequestSpec(OCSPStatusRequest.EMPTY_OCSP);116117final CertStatusRequest statusRequest;118119private CertStatusRequestSpec(CertStatusRequest statusRequest) {120this.statusRequest = statusRequest;121}122123private CertStatusRequestSpec(HandshakeContext hc,124ByteBuffer buffer) throws IOException {125// Is it a empty extension_data?126if (buffer.remaining() == 0) {127// server response128this.statusRequest = null;129return;130}131132if (buffer.remaining() < 1) {133throw hc.conContext.fatal(Alert.DECODE_ERROR,134new SSLProtocolException(135"Invalid status_request extension: insufficient data"));136}137138byte statusType = (byte)Record.getInt8(buffer);139byte[] encoded = new byte[buffer.remaining()];140if (encoded.length != 0) {141buffer.get(encoded);142}143if (statusType == CertStatusRequestType.OCSP.id) {144this.statusRequest = new OCSPStatusRequest(statusType, encoded);145} else {146if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {147SSLLogger.info(148"Unknown certificate status request " +149"(status type: " + statusType + ")");150}151152this.statusRequest = new CertStatusRequest(statusType, encoded);153}154}155156@Override157public String toString() {158return statusRequest == null ?159"<empty>" : statusRequest.toString();160}161}162163/**164* Defines the CertificateStatus response structure as outlined in165* RFC 6066. This will contain a status response type, plus a single,166* non-empty OCSP response in DER-encoded form.167*168* struct {169* CertificateStatusType status_type;170* select (status_type) {171* case ocsp: OCSPResponse;172* } response;173* } CertificateStatus;174*/175static final class CertStatusResponseSpec implements SSLExtensionSpec {176final CertStatusResponse statusResponse;177178private CertStatusResponseSpec(CertStatusResponse resp) {179this.statusResponse = resp;180}181182private CertStatusResponseSpec(HandshakeContext hc,183ByteBuffer buffer) throws IOException {184if (buffer.remaining() < 2) {185throw hc.conContext.fatal(Alert.DECODE_ERROR,186new SSLProtocolException(187"Invalid status_request extension: insufficient data"));188}189190// Get the status type (1 byte) and response data (vector)191byte type = (byte)Record.getInt8(buffer);192byte[] respData = Record.getBytes24(buffer);193194// Create the CertStatusResponse based on the type195if (type == CertStatusRequestType.OCSP.id) {196this.statusResponse = new OCSPStatusResponse(type, respData);197} else {198if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {199SSLLogger.info(200"Unknown certificate status response " +201"(status type: " + type + ")");202}203204this.statusResponse = new CertStatusResponse(type, respData);205}206}207208@Override209public String toString() {210return statusResponse == null ?211"<empty>" : statusResponse.toString();212}213}214215private static final216class CertStatusRequestStringizer implements SSLStringizer {217@Override218public String toString(HandshakeContext hc, ByteBuffer buffer) {219try {220return (new CertStatusRequestSpec(hc, buffer)).toString();221} catch (IOException ioe) {222// For debug logging only, so please swallow exceptions.223return ioe.getMessage();224}225}226}227228private static final229class CertStatusRespStringizer implements SSLStringizer {230@Override231public String toString(HandshakeContext hc, ByteBuffer buffer) {232try {233return (new CertStatusResponseSpec(hc, buffer)).toString();234} catch (IOException ioe) {235// For debug logging only, so please swallow exceptions.236return ioe.getMessage();237}238}239}240241static enum CertStatusRequestType {242OCSP ((byte)0x01, "ocsp"), // RFC 6066/6961243OCSP_MULTI ((byte)0x02, "ocsp_multi"); // RFC 6961244245final byte id;246final String name;247248private CertStatusRequestType(byte id, String name) {249this.id = id;250this.name = name;251}252253/**254* Returns the enum constant of the specified id (see RFC 6066).255*/256static CertStatusRequestType valueOf(byte id) {257for (CertStatusRequestType srt : CertStatusRequestType.values()) {258if (srt.id == id) {259return srt;260}261}262263return null;264}265266static String nameOf(byte id) {267for (CertStatusRequestType srt : CertStatusRequestType.values()) {268if (srt.id == id) {269return srt.name;270}271}272273return "UNDEFINED-CERT-STATUS-TYPE(" + id + ")";274}275}276277static class CertStatusRequest {278final byte statusType;279final byte[] encodedRequest;280281protected CertStatusRequest(byte statusType, byte[] encodedRequest) {282this.statusType = statusType;283this.encodedRequest = encodedRequest;284}285286@Override287public String toString() {288MessageFormat messageFormat = new MessageFormat(289"\"certificate status type\": {0}\n" +290"\"encoded certificate status\": '{'\n" +291"{1}\n" +292"'}'",293Locale.ENGLISH);294295HexDumpEncoder hexEncoder = new HexDumpEncoder();296String encoded = hexEncoder.encodeBuffer(encodedRequest);297298Object[] messageFields = {299CertStatusRequestType.nameOf(statusType),300Utilities.indent(encoded)301};302303return messageFormat.format(messageFields);304}305}306307/*308* RFC6066 defines the TLS extension,"status_request" (type 0x5),309* which allows the client to request that the server perform OCSP310* on the client's behalf.311*312* The RFC defines an OCSPStatusRequest structure:313*314* struct {315* ResponderID responder_id_list<0..2^16-1>;316* Extensions request_extensions;317* } OCSPStatusRequest;318*/319static final class OCSPStatusRequest extends CertStatusRequest {320static final OCSPStatusRequest EMPTY_OCSP;321static final OCSPStatusRequest EMPTY_OCSP_MULTI;322323final List<ResponderId> responderIds;324final List<Extension> extensions;325326static {327OCSPStatusRequest ocspReq = null;328OCSPStatusRequest multiReq = null;329330try {331ocspReq = new OCSPStatusRequest(332CertStatusRequestType.OCSP.id,333new byte[] {0x00, 0x00, 0x00, 0x00});334multiReq = new OCSPStatusRequest(335CertStatusRequestType.OCSP_MULTI.id,336new byte[] {0x00, 0x00, 0x00, 0x00});337} catch (IOException ioe) {338// unlikely339}340341EMPTY_OCSP = ocspReq;342EMPTY_OCSP_MULTI = multiReq;343}344345private OCSPStatusRequest(byte statusType,346byte[] encoded) throws IOException {347super(statusType, encoded);348349if (encoded == null || encoded.length < 4) {350// 2: length of responder_id_list351// +2: length of request_extensions352throw new SSLProtocolException(353"Invalid OCSP status request: insufficient data");354}355356List<ResponderId> rids = new ArrayList<>();357List<Extension> exts = new ArrayList<>();358ByteBuffer m = ByteBuffer.wrap(encoded);359360int ridListLen = Record.getInt16(m);361if (m.remaining() < (ridListLen + 2)) {362throw new SSLProtocolException(363"Invalid OCSP status request: insufficient data");364}365366int ridListBytesRemaining = ridListLen;367while (ridListBytesRemaining >= 2) { // 2: length of responder_id368byte[] ridBytes = Record.getBytes16(m);369try {370rids.add(new ResponderId(ridBytes));371} catch (IOException ioe) {372throw new SSLProtocolException(373"Invalid OCSP status request: invalid responder ID");374}375ridListBytesRemaining -= ridBytes.length + 2;376}377378if (ridListBytesRemaining != 0) {379throw new SSLProtocolException(380"Invalid OCSP status request: incomplete data");381}382383byte[] extListBytes = Record.getBytes16(m);384int extListLen = extListBytes.length;385if (extListLen > 0) {386try {387DerInputStream dis = new DerInputStream(extListBytes);388DerValue[] extSeqContents =389dis.getSequence(extListBytes.length);390for (DerValue extDerVal : extSeqContents) {391exts.add(new sun.security.x509.Extension(extDerVal));392}393} catch (IOException ioe) {394throw new SSLProtocolException(395"Invalid OCSP status request: invalid extension");396}397}398399this.responderIds = rids;400this.extensions = exts;401}402403@Override404public String toString() {405MessageFormat messageFormat = new MessageFormat(406"\"certificate status type\": {0}\n" +407"\"OCSP status request\": '{'\n" +408"{1}\n" +409"'}'",410Locale.ENGLISH);411412MessageFormat requestFormat = new MessageFormat(413"\"responder_id\": {0}\n" +414"\"request extensions\": '{'\n" +415"{1}\n" +416"'}'",417Locale.ENGLISH);418419String ridStr = "<empty>";420if (!responderIds.isEmpty()) {421ridStr = responderIds.toString();422}423424String extsStr = "<empty>";425if (!extensions.isEmpty()) {426StringBuilder extBuilder = new StringBuilder(512);427boolean isFirst = true;428for (Extension ext : this.extensions) {429if (isFirst) {430isFirst = false;431} else {432extBuilder.append(",\n");433}434extBuilder.append("{\n").435append(Utilities.indent(ext.toString())).436append("}");437}438439extsStr = extBuilder.toString();440}441442Object[] requestFields = {443ridStr,444Utilities.indent(extsStr)445};446String ocspStatusRequest = requestFormat.format(requestFields);447448Object[] messageFields = {449CertStatusRequestType.nameOf(statusType),450Utilities.indent(ocspStatusRequest)451};452453return messageFormat.format(messageFields);454}455}456457static class CertStatusResponse {458final byte statusType;459final byte[] encodedResponse;460461protected CertStatusResponse(byte statusType, byte[] respDer) {462this.statusType = statusType;463this.encodedResponse = respDer;464}465466byte[] toByteArray() throws IOException {467// Create a byte array large enough to handle the status_type468// field (1) + OCSP length (3) + OCSP data (variable)469byte[] outData = new byte[encodedResponse.length + 4];470ByteBuffer buf = ByteBuffer.wrap(outData);471Record.putInt8(buf, statusType);472Record.putBytes24(buf, encodedResponse);473return buf.array();474}475476@Override477public String toString() {478MessageFormat messageFormat = new MessageFormat(479"\"certificate status response type\": {0}\n" +480"\"encoded certificate status\": '{'\n" +481"{1}\n" +482"'}'",483Locale.ENGLISH);484485HexDumpEncoder hexEncoder = new HexDumpEncoder();486String encoded = hexEncoder.encodeBuffer(encodedResponse);487488Object[] messageFields = {489CertStatusRequestType.nameOf(statusType),490Utilities.indent(encoded)491};492493return messageFormat.format(messageFields);494}495}496497static final class OCSPStatusResponse extends CertStatusResponse {498final OCSPResponse ocspResponse;499500private OCSPStatusResponse(byte statusType,501byte[] encoded) throws IOException {502super(statusType, encoded);503504// The DER-encoded OCSP response must not be zero length505if (encoded == null || encoded.length < 1) {506throw new SSLProtocolException(507"Invalid OCSP status response: insufficient data");508}509510// Otherwise, make an OCSPResponse object from the data511ocspResponse = new OCSPResponse(encoded);512}513514@Override515public String toString() {516MessageFormat messageFormat = new MessageFormat(517"\"certificate status response type\": {0}\n" +518"\"OCSP status response\": '{'\n" +519"{1}\n" +520"'}'",521Locale.ENGLISH);522523Object[] messageFields = {524CertStatusRequestType.nameOf(statusType),525Utilities.indent(ocspResponse.toString())526};527528return messageFormat.format(messageFields);529}530}531532/**533* Network data producer of a "status_request" extension in the534* ClientHello handshake message.535*/536private static final537class CHCertStatusReqProducer implements HandshakeProducer {538// Prevent instantiation of this class.539private CHCertStatusReqProducer() {540// blank541}542543@Override544public byte[] produce(ConnectionContext context,545HandshakeMessage message) throws IOException {546// The producing happens in client side only.547ClientHandshakeContext chc = (ClientHandshakeContext)context;548549if (!chc.sslContext.isStaplingEnabled(true)) {550return null;551}552553if (!chc.sslConfig.isAvailable(SSLExtension.CH_STATUS_REQUEST)) {554if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {555SSLLogger.fine(556"Ignore unavailable extension: " +557SSLExtension.CH_STATUS_REQUEST.name);558}559return null;560}561562// Produce the extension.563//564// We are using empty OCSPStatusRequest at present. May extend to565// support specific responder or extensions later.566byte[] extData = new byte[] {0x01, 0x00, 0x00, 0x00, 0x00};567568// Update the context.569chc.handshakeExtensions.put(SSLExtension.CH_STATUS_REQUEST,570CertStatusRequestSpec.DEFAULT);571572return extData;573}574}575576/**577* Network data consumer of a "status_request" extension in the578* ClientHello handshake message.579*/580private static final581class CHCertStatusReqConsumer implements ExtensionConsumer {582// Prevent instantiation of this class.583private CHCertStatusReqConsumer() {584// blank585}586587@Override588public void consume(ConnectionContext context,589HandshakeMessage message, ByteBuffer buffer) throws IOException {590591// The consuming happens in server side only.592ServerHandshakeContext shc = (ServerHandshakeContext)context;593594if (!shc.sslConfig.isAvailable(SSLExtension.CH_STATUS_REQUEST)) {595if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {596SSLLogger.fine("Ignore unavailable extension: " +597SSLExtension.CH_STATUS_REQUEST.name);598}599return; // ignore the extension600}601602// Parse the extension.603CertStatusRequestSpec spec = new CertStatusRequestSpec(shc, buffer);604605// Update the context.606shc.handshakeExtensions.put(SSLExtension.CH_STATUS_REQUEST, spec);607if (!shc.isResumption &&608!shc.negotiatedProtocol.useTLS13PlusSpec()) {609shc.handshakeProducers.put(SSLHandshake.CERTIFICATE_STATUS.id,610SSLHandshake.CERTIFICATE_STATUS);611} // Otherwise, the certificate status presents in server cert.612613// No impact on session resumption.614}615}616617/**618* Network data producer of a "status_request" extension in the619* ServerHello handshake message.620*/621private static final622class SHCertStatusReqProducer implements HandshakeProducer {623// Prevent instantiation of this class.624private SHCertStatusReqProducer() {625// blank626}627628@Override629public byte[] produce(ConnectionContext context,630HandshakeMessage message) throws IOException {631// The producing happens in client side only.632ServerHandshakeContext shc = (ServerHandshakeContext)context;633634// The StaplingParameters in the ServerHandshakeContext will635// contain the info about what kind of stapling (if any) to636// perform and whether this status_request extension should be637// produced or the status_request_v2 (found in a different producer)638// No explicit check is required for isStaplingEnabled here. If639// it is false then stapleParams will be null. If it is true640// then stapleParams may or may not be false and the check below641// is sufficient.642if ((shc.stapleParams == null) ||643(shc.stapleParams.statusRespExt !=644SSLExtension.CH_STATUS_REQUEST)) {645return null; // Do not produce status_request in ServerHello646}647648// In response to "status_request" extension request only.649CertStatusRequestSpec spec = (CertStatusRequestSpec)650shc.handshakeExtensions.get(SSLExtension.CH_STATUS_REQUEST);651if (spec == null) {652// Ignore, no status_request extension requested.653if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {654SSLLogger.finest("Ignore unavailable extension: " +655SSLExtension.CH_STATUS_REQUEST.name);656}657658return null; // ignore the extension659}660661// Is it a session resuming?662if (shc.isResumption) {663if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {664SSLLogger.finest(665"No status_request response for session resuming");666}667668return null; // ignore the extension669}670671// The "extension_data" in the extended ServerHello handshake672// message MUST be empty.673byte[] extData = new byte[0];674675// Update the context.676shc.handshakeExtensions.put(SSLExtension.SH_STATUS_REQUEST,677CertStatusRequestSpec.DEFAULT);678679return extData;680}681}682683/**684* Network data consumer of a "status_request" extension in the685* ServerHello handshake message.686*/687private static final688class SHCertStatusReqConsumer implements ExtensionConsumer {689// Prevent instantiation of this class.690private SHCertStatusReqConsumer() {691// blank692}693694@Override695public void consume(ConnectionContext context,696HandshakeMessage message, ByteBuffer buffer) throws IOException {697698// The producing happens in client side only.699ClientHandshakeContext chc = (ClientHandshakeContext)context;700701// In response to "status_request" extension request only.702CertStatusRequestSpec requestedCsr = (CertStatusRequestSpec)703chc.handshakeExtensions.get(SSLExtension.CH_STATUS_REQUEST);704if (requestedCsr == null) {705throw chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,706"Unexpected status_request extension in ServerHello");707}708709// Parse the extension.710if (buffer.hasRemaining()) {711throw chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,712"Invalid status_request extension in ServerHello message: " +713"the extension data must be empty");714}715716// Update the context.717chc.handshakeExtensions.put(SSLExtension.SH_STATUS_REQUEST,718CertStatusRequestSpec.DEFAULT);719720// Since we've received a legitimate status_request in the721// ServerHello, stapling is active if it's been enabled.722chc.staplingActive = chc.sslContext.isStaplingEnabled(true);723if (chc.staplingActive) {724chc.handshakeConsumers.put(SSLHandshake.CERTIFICATE_STATUS.id,725SSLHandshake.CERTIFICATE_STATUS);726}727728// No impact on session resumption.729}730}731732/**733* The "status_request_v2" extension.734*735* RFC6961 defines the TLS extension,"status_request_v2" (type 0x5),736* which allows the client to request that the server perform OCSP737* on the client's behalf.738*739* The RFC defines an CertStatusReqItemV2 structure:740*741* struct {742* CertificateStatusType status_type;743* uint16 request_length;744* select (status_type) {745* case ocsp: OCSPStatusRequest;746* case ocsp_multi: OCSPStatusRequest;747* } request;748* } CertificateStatusRequestItemV2;749*750* enum { ocsp(1), ocsp_multi(2), (255) } CertificateStatusType;751* struct {752* ResponderID responder_id_list<0..2^16-1>;753* Extensions request_extensions;754* } OCSPStatusRequest;755*756* opaque ResponderID<1..2^16-1>;757* opaque Extensions<0..2^16-1>;758*759* struct {760* CertificateStatusRequestItemV2761* certificate_status_req_list<1..2^16-1>;762* } CertificateStatusRequestListV2;763*/764static final class CertStatusRequestV2Spec implements SSLExtensionSpec {765static final CertStatusRequestV2Spec DEFAULT =766new CertStatusRequestV2Spec(new CertStatusRequest[] {767OCSPStatusRequest.EMPTY_OCSP_MULTI});768769final CertStatusRequest[] certStatusRequests;770771private CertStatusRequestV2Spec(CertStatusRequest[] certStatusRequests) {772this.certStatusRequests = certStatusRequests;773}774775private CertStatusRequestV2Spec(HandshakeContext hc,776ByteBuffer message) throws IOException {777// Is it a empty extension_data?778if (message.remaining() == 0) {779// server response780this.certStatusRequests = new CertStatusRequest[0];781return;782}783784if (message.remaining() < 5) { // 2: certificate_status_req_list785// +1: status_type786// +2: request_length787throw hc.conContext.fatal(Alert.DECODE_ERROR,788new SSLProtocolException(789"Invalid status_request_v2 extension: insufficient data"));790}791792int listLen = Record.getInt16(message);793if (listLen <= 0) {794throw hc.conContext.fatal(Alert.DECODE_ERROR,795new SSLProtocolException(796"certificate_status_req_list length must be positive " +797"(received length: " + listLen + ")"));798}799800int remaining = listLen;801List<CertStatusRequest> statusRequests = new ArrayList<>();802while (remaining > 0) {803byte statusType = (byte)Record.getInt8(message);804int requestLen = Record.getInt16(message);805806if (message.remaining() < requestLen) {807throw hc.conContext.fatal(808Alert.DECODE_ERROR,809new SSLProtocolException(810"Invalid status_request_v2 extension: " +811"insufficient data (request_length=" + requestLen +812", remining=" + message.remaining() + ")"));813}814815byte[] encoded = new byte[requestLen];816if (encoded.length != 0) {817message.get(encoded);818}819remaining -= 3; // 1(status type) + 2(request_length) bytes820remaining -= requestLen;821822if (statusType == CertStatusRequestType.OCSP.id ||823statusType == CertStatusRequestType.OCSP_MULTI.id) {824if (encoded.length < 4) {825// 2: length of responder_id_list826// +2: length of request_extensions827throw hc.conContext.fatal(828Alert.DECODE_ERROR,829new SSLProtocolException(830"Invalid status_request_v2 extension: " +831"insufficient data"));832}833statusRequests.add(834new OCSPStatusRequest(statusType, encoded));835} else {836if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {837SSLLogger.info(838"Unknown certificate status request " +839"(status type: " + statusType + ")");840}841statusRequests.add(842new CertStatusRequest(statusType, encoded));843}844}845846certStatusRequests =847statusRequests.toArray(new CertStatusRequest[0]);848}849850@Override851public String toString() {852if (certStatusRequests == null || certStatusRequests.length == 0) {853return "<empty>";854} else {855MessageFormat messageFormat = new MessageFormat(856"\"cert status request\": '{'\n{0}\n'}'", Locale.ENGLISH);857858StringBuilder builder = new StringBuilder(512);859boolean isFirst = true;860for (CertStatusRequest csr : certStatusRequests) {861if (isFirst) {862isFirst = false;863} else {864builder.append(", ");865}866Object[] messageFields = {867Utilities.indent(csr.toString())868};869builder.append(messageFormat.format(messageFields));870}871872return builder.toString();873}874}875}876877private static final878class CertStatusRequestsStringizer implements SSLStringizer {879@Override880public String toString(HandshakeContext hc, ByteBuffer buffer) {881try {882return (new CertStatusRequestV2Spec(hc, buffer)).toString();883} catch (IOException ioe) {884// For debug logging only, so please swallow exceptions.885return ioe.getMessage();886}887}888}889890/**891* Network data producer of a "status_request_v2" extension in the892* ClientHello handshake message.893*/894private static final895class CHCertStatusReqV2Producer implements HandshakeProducer {896// Prevent instantiation of this class.897private CHCertStatusReqV2Producer() {898// blank899}900901@Override902public byte[] produce(ConnectionContext context,903HandshakeMessage message) throws IOException {904// The producing happens in client side only.905ClientHandshakeContext chc = (ClientHandshakeContext)context;906907if (!chc.sslContext.isStaplingEnabled(true)) {908return null;909}910911if (!chc.sslConfig.isAvailable(SSLExtension.CH_STATUS_REQUEST_V2)) {912if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {913SSLLogger.finest(914"Ignore unavailable status_request_v2 extension");915}916917return null;918}919920// Produce the extension.921//922// We are using empty OCSPStatusRequest at present. May extend to923// support specific responder or extensions later.924byte[] extData = new byte[] {9250x00, 0x07, 0x02, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00};926927// Update the context.928chc.handshakeExtensions.put(SSLExtension.CH_STATUS_REQUEST_V2,929CertStatusRequestV2Spec.DEFAULT);930931return extData;932}933}934935/**936* Network data consumer of a "status_request_v2" extension in the937* ClientHello handshake message.938*/939private static final940class CHCertStatusReqV2Consumer implements ExtensionConsumer {941// Prevent instantiation of this class.942private CHCertStatusReqV2Consumer() {943// blank944}945946@Override947public void consume(ConnectionContext context,948HandshakeMessage message, ByteBuffer buffer) throws IOException {949950// The consuming happens in server side only.951ServerHandshakeContext shc = (ServerHandshakeContext)context;952953if (!shc.sslConfig.isAvailable(SSLExtension.CH_STATUS_REQUEST_V2)) {954if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {955SSLLogger.finest(956"Ignore unavailable status_request_v2 extension");957}958959return; // ignore the extension960}961962// Parse the extension.963CertStatusRequestV2Spec spec = new CertStatusRequestV2Spec(shc, buffer);964965// Update the context.966shc.handshakeExtensions.put(SSLExtension.CH_STATUS_REQUEST_V2,967spec);968if (!shc.isResumption) {969shc.handshakeProducers.putIfAbsent(970SSLHandshake.CERTIFICATE_STATUS.id,971SSLHandshake.CERTIFICATE_STATUS);972}973974// No impact on session resumption.975}976}977978/**979* Network data producer of a "status_request_v2" extension in the980* ServerHello handshake message.981*/982private static final983class SHCertStatusReqV2Producer implements HandshakeProducer {984// Prevent instantiation of this class.985private SHCertStatusReqV2Producer() {986// blank987}988989@Override990public byte[] produce(ConnectionContext context,991HandshakeMessage message) throws IOException {992// The producing happens in client side only.993994ServerHandshakeContext shc = (ServerHandshakeContext)context;995// The StaplingParameters in the ServerHandshakeContext will996// contain the info about what kind of stapling (if any) to997// perform and whether this status_request extension should be998// produced or the status_request_v2 (found in a different producer)999// No explicit check is required for isStaplingEnabled here. If1000// it is false then stapleParams will be null. If it is true1001// then stapleParams may or may not be false and the check below1002// is sufficient.1003if ((shc.stapleParams == null) ||1004(shc.stapleParams.statusRespExt !=1005SSLExtension.CH_STATUS_REQUEST_V2)) {1006return null; // Do not produce status_request_v2 in SH1007}10081009// In response to "status_request_v2" extension request only1010CertStatusRequestV2Spec spec = (CertStatusRequestV2Spec)1011shc.handshakeExtensions.get(SSLExtension.CH_STATUS_REQUEST_V2);1012if (spec == null) {1013// Ignore, no status_request_v2 extension requested.1014if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {1015SSLLogger.finest(1016"Ignore unavailable status_request_v2 extension");1017}10181019return null; // ignore the extension1020}10211022// Is it a session resuming?1023if (shc.isResumption) {1024if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {1025SSLLogger.finest(1026"No status_request_v2 response for session resumption");1027}1028return null; // ignore the extension1029}10301031// The "extension_data" in the extended ServerHello handshake1032// message MUST be empty.1033byte[] extData = new byte[0];10341035// Update the context.1036shc.handshakeExtensions.put(SSLExtension.SH_STATUS_REQUEST_V2,1037CertStatusRequestV2Spec.DEFAULT);10381039return extData;1040}1041}10421043/**1044* Network data consumer of a "status_request_v2" extension in the1045* ServerHello handshake message.1046*/1047private static final1048class SHCertStatusReqV2Consumer implements ExtensionConsumer {1049// Prevent instantiation of this class.1050private SHCertStatusReqV2Consumer() {1051// blank1052}10531054@Override1055public void consume(ConnectionContext context,1056HandshakeMessage message, ByteBuffer buffer) throws IOException {10571058// The consumption happens in client side only.1059ClientHandshakeContext chc = (ClientHandshakeContext)context;10601061// In response to "status_request" extension request only1062CertStatusRequestV2Spec requestedCsr = (CertStatusRequestV2Spec)1063chc.handshakeExtensions.get(SSLExtension.CH_STATUS_REQUEST_V2);1064if (requestedCsr == null) {1065throw chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,1066"Unexpected status_request_v2 extension in ServerHello");1067}10681069// Parse the extension.1070if (buffer.hasRemaining()) {1071throw chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,1072"Invalid status_request_v2 extension in ServerHello: " +1073"the extension data must be empty");1074}10751076// Update the context.1077chc.handshakeExtensions.put(SSLExtension.SH_STATUS_REQUEST_V2,1078CertStatusRequestV2Spec.DEFAULT);10791080// Since we've received a legitimate status_request in the1081// ServerHello, stapling is active if it's been enabled. If it1082// is active, make sure we add the CertificateStatus message1083// consumer.1084chc.staplingActive = chc.sslContext.isStaplingEnabled(true);1085if (chc.staplingActive) {1086chc.handshakeConsumers.put(SSLHandshake.CERTIFICATE_STATUS.id,1087SSLHandshake.CERTIFICATE_STATUS);1088}10891090// No impact on session resumption.1091}1092}10931094private static final1095class CTCertStatusResponseProducer implements HandshakeProducer {1096// Prevent instantiation of this class.1097private CTCertStatusResponseProducer() {1098// blank1099}11001101@Override1102public byte[] produce(ConnectionContext context,1103HandshakeMessage message) throws IOException {1104ServerHandshakeContext shc = (ServerHandshakeContext)context;1105byte[] producedData;11061107// Stapling needs to be active and have valid data to proceed1108if (shc.stapleParams == null) {1109if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {1110SSLLogger.finest(1111"Stapling is disabled for this connection");1112}1113return null;1114}11151116// There needs to be a non-null CertificateEntry to proceed1117if (shc.currentCertEntry == null) {1118if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {1119SSLLogger.finest("Found null CertificateEntry in context");1120}1121return null;1122}11231124// Pull the certificate from the CertificateEntry and find1125// a response from the response map. If one exists we will1126// staple it.1127try {1128CertificateFactory cf = CertificateFactory.getInstance("X.509");1129X509Certificate x509Cert =1130(X509Certificate)cf.generateCertificate(1131new ByteArrayInputStream(1132shc.currentCertEntry.encoded));1133byte[] respBytes = shc.stapleParams.responseMap.get(x509Cert);1134if (respBytes == null) {1135// We're done with this entry. Clear it from the context1136if (SSLLogger.isOn &&1137SSLLogger.isOn("ssl,handshake,verbose")) {1138SSLLogger.finest("No status response found for " +1139x509Cert.getSubjectX500Principal());1140}1141shc.currentCertEntry = null;1142return null;1143}11441145// Build a proper response buffer from the stapling information1146if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake,verbose")) {1147SSLLogger.finest("Found status response for " +1148x509Cert.getSubjectX500Principal() +1149", response length: " + respBytes.length);1150}1151CertStatusResponse certResp = (shc.stapleParams.statReqType ==1152CertStatusRequestType.OCSP) ?1153new OCSPStatusResponse(shc.stapleParams.statReqType.id,1154respBytes) :1155new CertStatusResponse(shc.stapleParams.statReqType.id,1156respBytes);1157producedData = certResp.toByteArray();1158} catch (CertificateException ce) {1159throw shc.conContext.fatal(Alert.BAD_CERTIFICATE,1160"Failed to parse server certificates", ce);1161} catch (IOException ioe) {1162throw shc.conContext.fatal(Alert.BAD_CERT_STATUS_RESPONSE,1163"Failed to parse certificate status response", ioe);1164}11651166// Clear the pinned CertificateEntry from the context1167shc.currentCertEntry = null;1168return producedData;1169}1170}11711172private static final1173class CTCertStatusResponseConsumer implements ExtensionConsumer {1174// Prevent instantiation of this class.1175private CTCertStatusResponseConsumer() {1176// blank1177}11781179@Override1180public void consume(ConnectionContext context,1181HandshakeMessage message, ByteBuffer buffer) throws IOException {1182// The consumption happens in client side only.1183ClientHandshakeContext chc = (ClientHandshakeContext)context;11841185// Parse the extension.1186CertStatusResponseSpec spec = new CertStatusResponseSpec(chc, buffer);11871188if (chc.sslContext.isStaplingEnabled(true)) {1189// Activate stapling1190chc.staplingActive = true;1191} else {1192// Do no further processing of stapled responses1193return;1194}11951196// Get response list from the session. This is unmodifiable1197// so we need to create a new list. Then add this new response1198// to the end and submit it back to the session object.1199if ((chc.handshakeSession != null) && (!chc.isResumption)) {1200List<byte[]> respList = new ArrayList<>(1201chc.handshakeSession.getStatusResponses());1202respList.add(spec.statusResponse.encodedResponse);1203chc.handshakeSession.setStatusResponses(respList);1204} else {1205if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake,verbose")) {1206SSLLogger.finest(1207"Ignoring stapled data on resumed session");1208}1209}1210}1211}1212}121312141215