Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/src/share/classes/sun/security/ssl/CertStatusExtension.java
38830 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.misc.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(ByteBuffer buffer) throws IOException {124// Is it a empty extension_data?125if (buffer.remaining() == 0) {126// server response127this.statusRequest = null;128return;129}130131if (buffer.remaining() < 1) {132throw new SSLProtocolException(133"Invalid status_request extension: insufficient data");134}135136byte statusType = (byte)Record.getInt8(buffer);137byte[] encoded = new byte[buffer.remaining()];138if (encoded.length != 0) {139buffer.get(encoded);140}141if (statusType == CertStatusRequestType.OCSP.id) {142this.statusRequest = new OCSPStatusRequest(statusType, encoded);143} else {144if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {145SSLLogger.info(146"Unknown certificate status request " +147"(status type: " + statusType + ")");148}149150this.statusRequest = new CertStatusRequest(statusType, encoded);151}152}153154@Override155public String toString() {156return statusRequest == null ?157"<empty>" : statusRequest.toString();158}159}160161/**162* Defines the CertificateStatus response structure as outlined in163* RFC 6066. This will contain a status response type, plus a single,164* non-empty OCSP response in DER-encoded form.165*166* struct {167* CertificateStatusType status_type;168* select (status_type) {169* case ocsp: OCSPResponse;170* } response;171* } CertificateStatus;172*/173static final class CertStatusResponseSpec implements SSLExtensionSpec {174final CertStatusResponse statusResponse;175176private CertStatusResponseSpec(CertStatusResponse resp) {177this.statusResponse = resp;178}179180private CertStatusResponseSpec(ByteBuffer buffer) throws IOException {181if (buffer.remaining() < 2) {182throw new SSLProtocolException(183"Invalid status_request extension: insufficient data");184}185186// Get the status type (1 byte) and response data (vector)187byte type = (byte)Record.getInt8(buffer);188byte[] respData = Record.getBytes24(buffer);189190// Create the CertStatusResponse based on the type191if (type == CertStatusRequestType.OCSP.id) {192this.statusResponse = new OCSPStatusResponse(type, respData);193} else {194if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {195SSLLogger.info(196"Unknown certificate status response " +197"(status type: " + type + ")");198}199200this.statusResponse = new CertStatusResponse(type, respData);201}202}203204@Override205public String toString() {206return statusResponse == null ?207"<empty>" : statusResponse.toString();208}209}210211private static final212class CertStatusRequestStringizer implements SSLStringizer {213@Override214public String toString(ByteBuffer buffer) {215try {216return (new CertStatusRequestSpec(buffer)).toString();217} catch (IOException ioe) {218// For debug logging only, so please swallow exceptions.219return ioe.getMessage();220}221}222}223224private static final225class CertStatusRespStringizer implements SSLStringizer {226@Override227public String toString(ByteBuffer buffer) {228try {229return (new CertStatusResponseSpec(buffer)).toString();230} catch (IOException ioe) {231// For debug logging only, so please swallow exceptions.232return ioe.getMessage();233}234}235}236237static enum CertStatusRequestType {238OCSP ((byte)0x01, "ocsp"), // RFC 6066/6961239OCSP_MULTI ((byte)0x02, "ocsp_multi"); // RFC 6961240241final byte id;242final String name;243244private CertStatusRequestType(byte id, String name) {245this.id = id;246this.name = name;247}248249/**250* Returns the enum constant of the specified id (see RFC 6066).251*/252static CertStatusRequestType valueOf(byte id) {253for (CertStatusRequestType srt : CertStatusRequestType.values()) {254if (srt.id == id) {255return srt;256}257}258259return null;260}261262static String nameOf(byte id) {263for (CertStatusRequestType srt : CertStatusRequestType.values()) {264if (srt.id == id) {265return srt.name;266}267}268269return "UNDEFINED-CERT-STATUS-TYPE(" + id + ")";270}271}272273static class CertStatusRequest {274final byte statusType;275final byte[] encodedRequest;276277protected CertStatusRequest(byte statusType, byte[] encodedRequest) {278this.statusType = statusType;279this.encodedRequest = encodedRequest;280}281282@Override283public String toString() {284MessageFormat messageFormat = new MessageFormat(285"\"certificate status type\": {0}\n" +286"\"encoded certificate status\": '{'\n" +287"{1}\n" +288"'}'",289Locale.ENGLISH);290291HexDumpEncoder hexEncoder = new HexDumpEncoder();292String encoded = hexEncoder.encodeBuffer(encodedRequest);293294Object[] messageFields = {295CertStatusRequestType.nameOf(statusType),296Utilities.indent(encoded)297};298299return messageFormat.format(messageFields);300}301}302303/*304* RFC6066 defines the TLS extension,"status_request" (type 0x5),305* which allows the client to request that the server perform OCSP306* on the client's behalf.307*308* The RFC defines an OCSPStatusRequest structure:309*310* struct {311* ResponderID responder_id_list<0..2^16-1>;312* Extensions request_extensions;313* } OCSPStatusRequest;314*/315static final class OCSPStatusRequest extends CertStatusRequest {316static final OCSPStatusRequest EMPTY_OCSP;317static final OCSPStatusRequest EMPTY_OCSP_MULTI;318319final List<ResponderId> responderIds;320final List<Extension> extensions;321private final int ridListLen;322private final int extListLen;323324static {325OCSPStatusRequest ocspReq = null;326OCSPStatusRequest multiReq = null;327328try {329ocspReq = new OCSPStatusRequest(330CertStatusRequestType.OCSP.id,331new byte[] {0x00, 0x00, 0x00, 0x00});332multiReq = new OCSPStatusRequest(333CertStatusRequestType.OCSP_MULTI.id,334new byte[] {0x00, 0x00, 0x00, 0x00});335} catch (IOException ioe) {336// unlikely337}338339EMPTY_OCSP = ocspReq;340EMPTY_OCSP_MULTI = multiReq;341}342343private OCSPStatusRequest(byte statusType,344byte[] encoded) throws IOException {345super(statusType, encoded);346347if (encoded == null || encoded.length < 4) {348// 2: length of responder_id_list349// +2: length of request_extensions350throw new SSLProtocolException(351"Invalid OCSP status request: insufficient data");352}353354List<ResponderId> rids = new ArrayList<>();355List<Extension> exts = new ArrayList<>();356ByteBuffer m = ByteBuffer.wrap(encoded);357358this.ridListLen = Record.getInt16(m);359if (m.remaining() < (ridListLen + 2)) {360throw new SSLProtocolException(361"Invalid OCSP status request: insufficient data");362}363364int ridListBytesRemaining = ridListLen;365while (ridListBytesRemaining >= 2) { // 2: length of responder_id366byte[] ridBytes = Record.getBytes16(m);367try {368rids.add(new ResponderId(ridBytes));369} catch (IOException ioe) {370throw new SSLProtocolException(371"Invalid OCSP status request: invalid responder ID");372}373ridListBytesRemaining -= ridBytes.length + 2;374}375376if (ridListBytesRemaining != 0) {377throw new SSLProtocolException(378"Invalid OCSP status request: incomplete data");379}380381byte[] extListBytes = Record.getBytes16(m);382this.extListLen = extListBytes.length;383if (extListLen > 0) {384try {385DerInputStream dis = new DerInputStream(extListBytes);386DerValue[] extSeqContents =387dis.getSequence(extListBytes.length);388for (DerValue extDerVal : extSeqContents) {389exts.add(new sun.security.x509.Extension(extDerVal));390}391} catch (IOException ioe) {392throw new SSLProtocolException(393"Invalid OCSP status request: invalid extension");394}395}396397this.responderIds = rids;398this.extensions = exts;399}400401@Override402public String toString() {403MessageFormat messageFormat = new MessageFormat(404"\"certificate status type\": {0}\n" +405"\"OCSP status request\": '{'\n" +406"{1}\n" +407"'}'",408Locale.ENGLISH);409410MessageFormat requestFormat = new MessageFormat(411"\"responder_id\": {0}\n" +412"\"request extensions\": '{'\n" +413"{1}\n" +414"'}'",415Locale.ENGLISH);416417String ridStr = "<empty>";418if (!responderIds.isEmpty()) {419ridStr = responderIds.toString();420}421422String extsStr = "<empty>";423if (!extensions.isEmpty()) {424StringBuilder extBuilder = new StringBuilder(512);425boolean isFirst = true;426for (Extension ext : this.extensions) {427if (isFirst) {428isFirst = false;429} else {430extBuilder.append(",\n");431}432extBuilder.append("{\n").433append(Utilities.indent(ext.toString())).434append("}");435}436437extsStr = extBuilder.toString();438}439440Object[] requestFields = {441ridStr,442Utilities.indent(extsStr)443};444String ocspStatusRequest = requestFormat.format(requestFields);445446Object[] messageFields = {447CertStatusRequestType.nameOf(statusType),448Utilities.indent(ocspStatusRequest)449};450451return messageFormat.format(messageFields);452}453}454455static class CertStatusResponse {456final byte statusType;457final byte[] encodedResponse;458459protected CertStatusResponse(byte statusType, byte[] respDer) {460this.statusType = statusType;461this.encodedResponse = respDer;462}463464byte[] toByteArray() throws IOException {465// Create a byte array large enough to handle the status_type466// field (1) + OCSP length (3) + OCSP data (variable)467byte[] outData = new byte[encodedResponse.length + 4];468ByteBuffer buf = ByteBuffer.wrap(outData);469Record.putInt8(buf, statusType);470Record.putBytes24(buf, encodedResponse);471return buf.array();472}473474@Override475public String toString() {476MessageFormat messageFormat = new MessageFormat(477"\"certificate status response type\": {0}\n" +478"\"encoded certificate status\": '{'\n" +479"{1}\n" +480"'}'",481Locale.ENGLISH);482483HexDumpEncoder hexEncoder = new HexDumpEncoder();484String encoded = hexEncoder.encodeBuffer(encodedResponse);485486Object[] messageFields = {487CertStatusRequestType.nameOf(statusType),488Utilities.indent(encoded)489};490491return messageFormat.format(messageFields);492}493}494495static final class OCSPStatusResponse extends CertStatusResponse {496final OCSPResponse ocspResponse;497498private OCSPStatusResponse(byte statusType,499byte[] encoded) throws IOException {500super(statusType, encoded);501502// The DER-encoded OCSP response must not be zero length503if (encoded == null || encoded.length < 1) {504throw new SSLProtocolException(505"Invalid OCSP status response: insufficient data");506}507508// Otherwise, make an OCSPResponse object from the data509ocspResponse = new OCSPResponse(encoded);510}511512@Override513public String toString() {514MessageFormat messageFormat = new MessageFormat(515"\"certificate status response type\": {0}\n" +516"\"OCSP status response\": '{'\n" +517"{1}\n" +518"'}'",519Locale.ENGLISH);520521Object[] messageFields = {522CertStatusRequestType.nameOf(statusType),523Utilities.indent(ocspResponse.toString())524};525526return messageFormat.format(messageFields);527}528}529530/**531* Network data producer of a "status_request" extension in the532* ClientHello handshake message.533*/534private static final535class CHCertStatusReqProducer implements HandshakeProducer {536// Prevent instantiation of this class.537private CHCertStatusReqProducer() {538// blank539}540541@Override542public byte[] produce(ConnectionContext context,543HandshakeMessage message) throws IOException {544// The producing happens in client side only.545ClientHandshakeContext chc = (ClientHandshakeContext)context;546547if (!chc.sslContext.isStaplingEnabled(true)) {548return null;549}550551if (!chc.sslConfig.isAvailable(SSLExtension.CH_STATUS_REQUEST)) {552if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {553SSLLogger.fine(554"Ignore unavailable extension: " +555SSLExtension.CH_STATUS_REQUEST.name);556}557return null;558}559560// Produce the extension.561//562// We are using empty OCSPStatusRequest at present. May extend to563// support specific responder or extensions later.564byte[] extData = new byte[] {0x01, 0x00, 0x00, 0x00, 0x00};565566// Update the context.567chc.handshakeExtensions.put(SSLExtension.CH_STATUS_REQUEST,568CertStatusRequestSpec.DEFAULT);569570return extData;571}572}573574/**575* Network data consumer of a "status_request" extension in the576* ClientHello handshake message.577*/578private static final579class CHCertStatusReqConsumer implements ExtensionConsumer {580// Prevent instantiation of this class.581private CHCertStatusReqConsumer() {582// blank583}584585@Override586public void consume(ConnectionContext context,587HandshakeMessage message, ByteBuffer buffer) throws IOException {588589// The consuming happens in server side only.590ServerHandshakeContext shc = (ServerHandshakeContext)context;591592if (!shc.sslConfig.isAvailable(SSLExtension.CH_STATUS_REQUEST)) {593if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {594SSLLogger.fine("Ignore unavailable extension: " +595SSLExtension.CH_STATUS_REQUEST.name);596}597return; // ignore the extension598}599600// Parse the extension.601CertStatusRequestSpec spec;602try {603spec = new CertStatusRequestSpec(buffer);604} catch (IOException ioe) {605throw shc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, ioe);606}607608// Update the context.609shc.handshakeExtensions.put(SSLExtension.CH_STATUS_REQUEST, spec);610if (!shc.isResumption &&611!shc.negotiatedProtocol.useTLS13PlusSpec()) {612shc.handshakeProducers.put(SSLHandshake.CERTIFICATE_STATUS.id,613SSLHandshake.CERTIFICATE_STATUS);614} // Otherwise, the certificate status presents in server cert.615616// No impact on session resumption.617}618}619620/**621* Network data producer of a "status_request" extension in the622* ServerHello handshake message.623*/624private static final625class SHCertStatusReqProducer implements HandshakeProducer {626// Prevent instantiation of this class.627private SHCertStatusReqProducer() {628// blank629}630631@Override632public byte[] produce(ConnectionContext context,633HandshakeMessage message) throws IOException {634// The producing happens in client side only.635ServerHandshakeContext shc = (ServerHandshakeContext)context;636637// The StaplingParameters in the ServerHandshakeContext will638// contain the info about what kind of stapling (if any) to639// perform and whether this status_request extension should be640// produced or the status_request_v2 (found in a different producer)641// No explicit check is required for isStaplingEnabled here. If642// it is false then stapleParams will be null. If it is true643// then stapleParams may or may not be false and the check below644// is sufficient.645if ((shc.stapleParams == null) ||646(shc.stapleParams.statusRespExt !=647SSLExtension.CH_STATUS_REQUEST)) {648return null; // Do not produce status_request in ServerHello649}650651// In response to "status_request" extension request only.652CertStatusRequestSpec spec = (CertStatusRequestSpec)653shc.handshakeExtensions.get(SSLExtension.CH_STATUS_REQUEST);654if (spec == null) {655// Ignore, no status_request extension requested.656if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {657SSLLogger.finest("Ignore unavailable extension: " +658SSLExtension.CH_STATUS_REQUEST.name);659}660661return null; // ignore the extension662}663664// Is it a session resuming?665if (shc.isResumption) {666if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {667SSLLogger.finest(668"No status_request response for session resuming");669}670671return null; // ignore the extension672}673674// The "extension_data" in the extended ServerHello handshake675// message MUST be empty.676byte[] extData = new byte[0];677678// Update the context.679shc.handshakeExtensions.put(SSLExtension.SH_STATUS_REQUEST,680CertStatusRequestSpec.DEFAULT);681682return extData;683}684}685686/**687* Network data consumer of a "status_request" extension in the688* ServerHello handshake message.689*/690private static final691class SHCertStatusReqConsumer implements ExtensionConsumer {692// Prevent instantiation of this class.693private SHCertStatusReqConsumer() {694// blank695}696697@Override698public void consume(ConnectionContext context,699HandshakeMessage message, ByteBuffer buffer) throws IOException {700701// The producing happens in client side only.702ClientHandshakeContext chc = (ClientHandshakeContext)context;703704// In response to "status_request" extension request only.705CertStatusRequestSpec requestedCsr = (CertStatusRequestSpec)706chc.handshakeExtensions.get(SSLExtension.CH_STATUS_REQUEST);707if (requestedCsr == null) {708throw chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,709"Unexpected status_request extension in ServerHello");710}711712// Parse the extension.713if (buffer.hasRemaining()) {714throw chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,715"Invalid status_request extension in ServerHello message: " +716"the extension data must be empty");717}718719// Update the context.720chc.handshakeExtensions.put(SSLExtension.SH_STATUS_REQUEST,721CertStatusRequestSpec.DEFAULT);722723// Since we've received a legitimate status_request in the724// ServerHello, stapling is active if it's been enabled.725chc.staplingActive = chc.sslContext.isStaplingEnabled(true);726if (chc.staplingActive) {727chc.handshakeConsumers.put(SSLHandshake.CERTIFICATE_STATUS.id,728SSLHandshake.CERTIFICATE_STATUS);729}730731// No impact on session resumption.732}733}734735/**736* The "status_request_v2" extension.737*738* RFC6961 defines the TLS extension,"status_request_v2" (type 0x5),739* which allows the client to request that the server perform OCSP740* on the client's behalf.741*742* The RFC defines an CertStatusReqItemV2 structure:743*744* struct {745* CertificateStatusType status_type;746* uint16 request_length;747* select (status_type) {748* case ocsp: OCSPStatusRequest;749* case ocsp_multi: OCSPStatusRequest;750* } request;751* } CertificateStatusRequestItemV2;752*753* enum { ocsp(1), ocsp_multi(2), (255) } CertificateStatusType;754* struct {755* ResponderID responder_id_list<0..2^16-1>;756* Extensions request_extensions;757* } OCSPStatusRequest;758*759* opaque ResponderID<1..2^16-1>;760* opaque Extensions<0..2^16-1>;761*762* struct {763* CertificateStatusRequestItemV2764* certificate_status_req_list<1..2^16-1>;765* } CertificateStatusRequestListV2;766*/767static final class CertStatusRequestV2Spec implements SSLExtensionSpec {768static final CertStatusRequestV2Spec DEFAULT =769new CertStatusRequestV2Spec(new CertStatusRequest[] {770OCSPStatusRequest.EMPTY_OCSP_MULTI});771772final CertStatusRequest[] certStatusRequests;773774private CertStatusRequestV2Spec(CertStatusRequest[] certStatusRequests) {775this.certStatusRequests = certStatusRequests;776}777778private CertStatusRequestV2Spec(ByteBuffer message) throws IOException {779// Is it a empty extension_data?780if (message.remaining() == 0) {781// server response782this.certStatusRequests = new CertStatusRequest[0];783return;784}785786if (message.remaining() < 5) { // 2: certificate_status_req_list787// +1: status_type788// +2: request_length789throw new SSLProtocolException(790"Invalid status_request_v2 extension: insufficient data");791}792793int listLen = Record.getInt16(message);794if (listLen <= 0) {795throw new 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 new SSLProtocolException(808"Invalid status_request_v2 extension: " +809"insufficient data (request_length=" + requestLen +810", remining=" + message.remaining() + ")");811}812813byte[] encoded = new byte[requestLen];814if (encoded.length != 0) {815message.get(encoded);816}817remaining -= 3; // 1(status type) + 2(request_length) bytes818remaining -= requestLen;819820if (statusType == CertStatusRequestType.OCSP.id ||821statusType == CertStatusRequestType.OCSP_MULTI.id) {822if (encoded.length < 4) {823// 2: length of responder_id_list824// +2: length of request_extensions825throw new SSLProtocolException(826"Invalid status_request_v2 extension: " +827"insufficient data");828}829statusRequests.add(830new OCSPStatusRequest(statusType, encoded));831} else {832if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {833SSLLogger.info(834"Unknown certificate status request " +835"(status type: " + statusType + ")");836}837statusRequests.add(838new CertStatusRequest(statusType, encoded));839}840}841842certStatusRequests =843statusRequests.toArray(new CertStatusRequest[0]);844}845846@Override847public String toString() {848if (certStatusRequests == null || certStatusRequests.length == 0) {849return "<empty>";850} else {851MessageFormat messageFormat = new MessageFormat(852"\"cert status request\": '{'\n{0}\n'}'", Locale.ENGLISH);853854StringBuilder builder = new StringBuilder(512);855boolean isFirst = true;856for (CertStatusRequest csr : certStatusRequests) {857if (isFirst) {858isFirst = false;859} else {860builder.append(", ");861}862Object[] messageFields = {863Utilities.indent(csr.toString())864};865builder.append(messageFormat.format(messageFields));866}867868return builder.toString();869}870}871}872873private static final874class CertStatusRequestsStringizer implements SSLStringizer {875@Override876public String toString(ByteBuffer buffer) {877try {878return (new CertStatusRequestV2Spec(buffer)).toString();879} catch (IOException ioe) {880// For debug logging only, so please swallow exceptions.881return ioe.getMessage();882}883}884}885886/**887* Network data producer of a "status_request_v2" extension in the888* ClientHello handshake message.889*/890private static final891class CHCertStatusReqV2Producer implements HandshakeProducer {892// Prevent instantiation of this class.893private CHCertStatusReqV2Producer() {894// blank895}896897@Override898public byte[] produce(ConnectionContext context,899HandshakeMessage message) throws IOException {900// The producing happens in client side only.901ClientHandshakeContext chc = (ClientHandshakeContext)context;902903if (!chc.sslContext.isStaplingEnabled(true)) {904return null;905}906907if (!chc.sslConfig.isAvailable(SSLExtension.CH_STATUS_REQUEST_V2)) {908if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {909SSLLogger.finest(910"Ignore unavailable status_request_v2 extension");911}912913return null;914}915916// Produce the extension.917//918// We are using empty OCSPStatusRequest at present. May extend to919// support specific responder or extensions later.920byte[] extData = new byte[] {9210x00, 0x07, 0x02, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00};922923// Update the context.924chc.handshakeExtensions.put(SSLExtension.CH_STATUS_REQUEST_V2,925CertStatusRequestV2Spec.DEFAULT);926927return extData;928}929}930931/**932* Network data consumer of a "status_request_v2" extension in the933* ClientHello handshake message.934*/935private static final936class CHCertStatusReqV2Consumer implements ExtensionConsumer {937// Prevent instantiation of this class.938private CHCertStatusReqV2Consumer() {939// blank940}941942@Override943public void consume(ConnectionContext context,944HandshakeMessage message, ByteBuffer buffer) throws IOException {945946// The consuming happens in server side only.947ServerHandshakeContext shc = (ServerHandshakeContext)context;948949if (!shc.sslConfig.isAvailable(SSLExtension.CH_STATUS_REQUEST_V2)) {950if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {951SSLLogger.finest(952"Ignore unavailable status_request_v2 extension");953}954955return; // ignore the extension956}957958// Parse the extension.959CertStatusRequestV2Spec spec;960try {961spec = new CertStatusRequestV2Spec(buffer);962} catch (IOException ioe) {963throw shc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, ioe);964}965966// Update the context.967shc.handshakeExtensions.put(SSLExtension.CH_STATUS_REQUEST_V2,968spec);969if (!shc.isResumption) {970shc.handshakeProducers.putIfAbsent(971SSLHandshake.CERTIFICATE_STATUS.id,972SSLHandshake.CERTIFICATE_STATUS);973}974975// No impact on session resumption.976}977}978979/**980* Network data producer of a "status_request_v2" extension in the981* ServerHello handshake message.982*/983private static final984class SHCertStatusReqV2Producer implements HandshakeProducer {985// Prevent instantiation of this class.986private SHCertStatusReqV2Producer() {987// blank988}989990@Override991public byte[] produce(ConnectionContext context,992HandshakeMessage message) throws IOException {993// The producing happens in client side only.994995ServerHandshakeContext shc = (ServerHandshakeContext)context;996// The StaplingParameters in the ServerHandshakeContext will997// contain the info about what kind of stapling (if any) to998// perform and whether this status_request extension should be999// produced or the status_request_v2 (found in a different producer)1000// No explicit check is required for isStaplingEnabled here. If1001// it is false then stapleParams will be null. If it is true1002// then stapleParams may or may not be false and the check below1003// is sufficient.1004if ((shc.stapleParams == null) ||1005(shc.stapleParams.statusRespExt !=1006SSLExtension.CH_STATUS_REQUEST_V2)) {1007return null; // Do not produce status_request_v2 in SH1008}10091010// In response to "status_request_v2" extension request only1011CertStatusRequestV2Spec spec = (CertStatusRequestV2Spec)1012shc.handshakeExtensions.get(SSLExtension.CH_STATUS_REQUEST_V2);1013if (spec == null) {1014// Ignore, no status_request_v2 extension requested.1015if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {1016SSLLogger.finest(1017"Ignore unavailable status_request_v2 extension");1018}10191020return null; // ignore the extension1021}10221023// Is it a session resuming?1024if (shc.isResumption) {1025if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {1026SSLLogger.finest(1027"No status_request_v2 response for session resumption");1028}1029return null; // ignore the extension1030}10311032// The "extension_data" in the extended ServerHello handshake1033// message MUST be empty.1034byte[] extData = new byte[0];10351036// Update the context.1037shc.handshakeExtensions.put(SSLExtension.SH_STATUS_REQUEST_V2,1038CertStatusRequestV2Spec.DEFAULT);10391040return extData;1041}1042}10431044/**1045* Network data consumer of a "status_request_v2" extension in the1046* ServerHello handshake message.1047*/1048private static final1049class SHCertStatusReqV2Consumer implements ExtensionConsumer {1050// Prevent instantiation of this class.1051private SHCertStatusReqV2Consumer() {1052// blank1053}10541055@Override1056public void consume(ConnectionContext context,1057HandshakeMessage message, ByteBuffer buffer) throws IOException {10581059// The consumption happens in client side only.1060ClientHandshakeContext chc = (ClientHandshakeContext)context;10611062// In response to "status_request" extension request only1063CertStatusRequestV2Spec requestedCsr = (CertStatusRequestV2Spec)1064chc.handshakeExtensions.get(SSLExtension.CH_STATUS_REQUEST_V2);1065if (requestedCsr == null) {1066throw chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,1067"Unexpected status_request_v2 extension in ServerHello");1068}10691070// Parse the extension.1071if (buffer.hasRemaining()) {1072throw chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,1073"Invalid status_request_v2 extension in ServerHello: " +1074"the extension data must be empty");1075}10761077// Update the context.1078chc.handshakeExtensions.put(SSLExtension.SH_STATUS_REQUEST_V2,1079CertStatusRequestV2Spec.DEFAULT);10801081// Since we've received a legitimate status_request in the1082// ServerHello, stapling is active if it's been enabled. If it1083// is active, make sure we add the CertificateStatus message1084// consumer.1085chc.staplingActive = chc.sslContext.isStaplingEnabled(true);1086if (chc.staplingActive) {1087chc.handshakeConsumers.put(SSLHandshake.CERTIFICATE_STATUS.id,1088SSLHandshake.CERTIFICATE_STATUS);1089}10901091// No impact on session resumption.1092}1093}10941095private static final1096class CTCertStatusResponseProducer implements HandshakeProducer {1097// Prevent instantiation of this class.1098private CTCertStatusResponseProducer() {1099// blank1100}11011102@Override1103public byte[] produce(ConnectionContext context,1104HandshakeMessage message) throws IOException {1105ServerHandshakeContext shc = (ServerHandshakeContext)context;1106byte[] producedData = null;11071108// Stapling needs to be active and have valid data to proceed1109if (shc.stapleParams == null) {1110if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {1111SSLLogger.finest(1112"Stapling is disabled for this connection");1113}1114return null;1115}11161117// There needs to be a non-null CertificateEntry to proceed1118if (shc.currentCertEntry == null) {1119if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {1120SSLLogger.finest("Found null CertificateEntry in context");1121}1122return null;1123}11241125// Pull the certificate from the CertificateEntry and find1126// a response from the response map. If one exists we will1127// staple it.1128try {1129CertificateFactory cf = CertificateFactory.getInstance("X.509");1130X509Certificate x509Cert =1131(X509Certificate)cf.generateCertificate(1132new ByteArrayInputStream(1133shc.currentCertEntry.encoded));1134byte[] respBytes = shc.stapleParams.responseMap.get(x509Cert);1135if (respBytes == null) {1136// We're done with this entry. Clear it from the context1137if (SSLLogger.isOn &&1138SSLLogger.isOn("ssl,handshake,verbose")) {1139SSLLogger.finest("No status response found for " +1140x509Cert.getSubjectX500Principal());1141}1142shc.currentCertEntry = null;1143return null;1144}11451146// Build a proper response buffer from the stapling information1147if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake,verbose")) {1148SSLLogger.finest("Found status response for " +1149x509Cert.getSubjectX500Principal() +1150", response length: " + respBytes.length);1151}1152CertStatusResponse certResp = (shc.stapleParams.statReqType ==1153CertStatusRequestType.OCSP) ?1154new OCSPStatusResponse(shc.stapleParams.statReqType.id,1155respBytes) :1156new CertStatusResponse(shc.stapleParams.statReqType.id,1157respBytes);1158producedData = certResp.toByteArray();1159} catch (CertificateException ce) {1160throw shc.conContext.fatal(Alert.BAD_CERTIFICATE,1161"Failed to parse server certificates", ce);1162} catch (IOException ioe) {1163throw shc.conContext.fatal(Alert.BAD_CERT_STATUS_RESPONSE,1164"Failed to parse certificate status response", ioe);1165}11661167// Clear the pinned CertificateEntry from the context1168shc.currentCertEntry = null;1169return producedData;1170}1171}11721173private static final1174class CTCertStatusResponseConsumer implements ExtensionConsumer {1175// Prevent instantiation of this class.1176private CTCertStatusResponseConsumer() {1177// blank1178}11791180@Override1181public void consume(ConnectionContext context,1182HandshakeMessage message, ByteBuffer buffer) throws IOException {1183// The consumption happens in client side only.1184ClientHandshakeContext chc = (ClientHandshakeContext)context;11851186// Parse the extension.1187CertStatusResponseSpec spec;1188try {1189spec = new CertStatusResponseSpec(buffer);1190} catch (IOException ioe) {1191throw chc.conContext.fatal(Alert.DECODE_ERROR, ioe);1192}11931194if (chc.sslContext.isStaplingEnabled(true)) {1195// Activate stapling1196chc.staplingActive = true;1197} else {1198// Do no further processing of stapled responses1199return;1200}12011202// Get response list from the session. This is unmodifiable1203// so we need to create a new list. Then add this new response1204// to the end and submit it back to the session object.1205if ((chc.handshakeSession != null) && (!chc.isResumption)) {1206List<byte[]> respList = new ArrayList<>(1207chc.handshakeSession.getStatusResponses());1208respList.add(spec.statusResponse.encodedResponse);1209chc.handshakeSession.setStatusResponses(respList);1210} else {1211if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake,verbose")) {1212SSLLogger.finest(1213"Ignoring stapled data on resumed session");1214}1215}1216}1217}1218}121912201221