Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/openjdk-multiarch-jdk8u
Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/src/share/classes/sun/security/ssl/CertStatusExtension.java
38830 views
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.misc.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
120
private CertStatusRequestSpec(CertStatusRequest statusRequest) {
121
this.statusRequest = statusRequest;
122
}
123
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
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
177
private CertStatusResponseSpec(CertStatusResponse resp) {
178
this.statusResponse = resp;
179
}
180
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
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
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
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
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
*/
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
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
278
protected CertStatusRequest(byte statusType, byte[] encodedRequest) {
279
this.statusType = statusType;
280
this.encodedRequest = encodedRequest;
281
}
282
283
@Override
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
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
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
460
protected CertStatusResponse(byte statusType, byte[] respDer) {
461
this.statusType = statusType;
462
this.encodedResponse = respDer;
463
}
464
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
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
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
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.
538
private CHCertStatusReqProducer() {
539
// blank
540
}
541
542
@Override
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.
582
private CHCertStatusReqConsumer() {
583
// blank
584
}
585
586
@Override
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.
628
private SHCertStatusReqProducer() {
629
// blank
630
}
631
632
@Override
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.
694
private SHCertStatusReqConsumer() {
695
// blank
696
}
697
698
@Override
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
775
private CertStatusRequestV2Spec(CertStatusRequest[] certStatusRequests) {
776
this.certStatusRequests = certStatusRequests;
777
}
778
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
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
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.
894
private CHCertStatusReqV2Producer() {
895
// blank
896
}
897
898
@Override
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.
939
private CHCertStatusReqV2Consumer() {
940
// blank
941
}
942
943
@Override
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.
987
private SHCertStatusReqV2Producer() {
988
// blank
989
}
990
991
@Override
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.
1052
private SHCertStatusReqV2Consumer() {
1053
// blank
1054
}
1055
1056
@Override
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.
1099
private CTCertStatusResponseProducer() {
1100
// blank
1101
}
1102
1103
@Override
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.
1177
private CTCertStatusResponseConsumer() {
1178
// blank
1179
}
1180
1181
@Override
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
1221