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