Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/mobile
Path: blob/master/test/jdk/javax/net/ssl/SSLSession/ResumeTLS13withSNI.java
41152 views
1
/*
2
* Copyright (c) 2018, 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.
8
*
9
* This code is distributed in the hope that it will be useful, but WITHOUT
10
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12
* version 2 for more details (a copy is included in the LICENSE file that
13
* accompanied this code).
14
*
15
* You should have received a copy of the GNU General Public License version
16
* 2 along with this work; if not, write to the Free Software Foundation,
17
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18
*
19
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20
* or visit www.oracle.com if you need additional information or have any
21
* questions.
22
*/
23
24
// SunJSSE does not support dynamic system properties, no way to re-use
25
// system properties in samevm/agentvm mode.
26
27
/*
28
* @test
29
* @bug 8211806
30
* @summary TLS 1.3 handshake server name indication is missing on a session resume
31
* @run main/othervm ResumeTLS13withSNI
32
*/
33
34
import javax.net.ssl.*;
35
import javax.net.ssl.SSLEngineResult.*;
36
import java.io.*;
37
import java.security.*;
38
import java.nio.*;
39
import java.util.List;
40
41
public class ResumeTLS13withSNI {
42
43
/*
44
* Enables logging of the SSLEngine operations.
45
*/
46
private static final boolean logging = false;
47
48
/*
49
* Enables the JSSE system debugging system property:
50
*
51
* -Djavax.net.debug=ssl:handshake
52
*
53
* This gives a lot of low-level information about operations underway,
54
* including specific handshake messages, and might be best examined
55
* after gaining some familiarity with this application.
56
*/
57
private static final boolean debug = true;
58
59
private static final ByteBuffer clientOut =
60
ByteBuffer.wrap("Hi Server, I'm Client".getBytes());
61
private static final ByteBuffer serverOut =
62
ByteBuffer.wrap("Hello Client, I'm Server".getBytes());
63
64
/*
65
* The following is to set up the keystores.
66
*/
67
private static final String pathToStores = "../etc";
68
private static final String keyStoreFile = "keystore";
69
private static final String trustStoreFile = "truststore";
70
private static final char[] passphrase = "passphrase".toCharArray();
71
72
private static final String keyFilename =
73
System.getProperty("test.src", ".") + "/" + pathToStores +
74
"/" + keyStoreFile;
75
private static final String trustFilename =
76
System.getProperty("test.src", ".") + "/" + pathToStores +
77
"/" + trustStoreFile;
78
79
private static final String HOST_NAME = "arf.yak.foo";
80
private static final SNIHostName SNI_NAME = new SNIHostName(HOST_NAME);
81
private static final SNIMatcher SNI_MATCHER =
82
SNIHostName.createSNIMatcher("arf\\.yak\\.foo");
83
84
/*
85
* Main entry point for this test.
86
*/
87
public static void main(String args[]) throws Exception {
88
if (debug) {
89
System.setProperty("javax.net.debug", "ssl:handshake");
90
}
91
92
KeyManagerFactory kmf = makeKeyManagerFactory(keyFilename,
93
passphrase);
94
TrustManagerFactory tmf = makeTrustManagerFactory(trustFilename,
95
passphrase);
96
97
SSLContext sslCtx = SSLContext.getInstance("TLS");
98
sslCtx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
99
100
// Make client and server engines, then customize as needed
101
SSLEngine clientEngine = makeEngine(sslCtx, kmf, tmf, true);
102
SSLParameters cliSSLParams = clientEngine.getSSLParameters();
103
cliSSLParams.setServerNames(List.of(SNI_NAME));
104
clientEngine.setSSLParameters(cliSSLParams);
105
clientEngine.setEnabledProtocols(new String[] { "TLSv1.3" });
106
107
SSLEngine serverEngine = makeEngine(sslCtx, kmf, tmf, false);
108
SSLParameters servSSLParams = serverEngine.getSSLParameters();
109
servSSLParams.setSNIMatchers(List.of(SNI_MATCHER));
110
serverEngine.setSSLParameters(servSSLParams);
111
112
initialHandshake(clientEngine, serverEngine);
113
114
// Create a new client-side engine which can initiate TLS session
115
// resumption
116
SSLEngine newCliEngine = makeEngine(sslCtx, kmf, tmf, true);
117
newCliEngine.setEnabledProtocols(new String[] { "TLSv1.3" });
118
ByteBuffer resCliHello = getResumptionClientHello(newCliEngine);
119
120
dumpBuffer("Resumed ClientHello Data", resCliHello);
121
122
// Parse the client hello message and make sure it is a resumption
123
// hello and has SNI in it.
124
checkResumedClientHelloSNI(resCliHello);
125
}
126
127
/*
128
* Run the test.
129
*
130
* Sit in a tight loop, both engines calling wrap/unwrap regardless
131
* of whether data is available or not. We do this until both engines
132
* report back they are closed.
133
*
134
* The main loop handles all of the I/O phases of the SSLEngine's
135
* lifetime:
136
*
137
* initial handshaking
138
* application data transfer
139
* engine closing
140
*
141
* One could easily separate these phases into separate
142
* sections of code.
143
*/
144
private static void initialHandshake(SSLEngine clientEngine,
145
SSLEngine serverEngine) throws Exception {
146
boolean dataDone = false;
147
148
// Create all the buffers
149
SSLSession session = clientEngine.getSession();
150
int appBufferMax = session.getApplicationBufferSize();
151
int netBufferMax = session.getPacketBufferSize();
152
ByteBuffer clientIn = ByteBuffer.allocate(appBufferMax + 50);
153
ByteBuffer serverIn = ByteBuffer.allocate(appBufferMax + 50);
154
ByteBuffer cTOs = ByteBuffer.allocateDirect(netBufferMax);
155
ByteBuffer sTOc = ByteBuffer.allocateDirect(netBufferMax);
156
157
// results from client's last operation
158
SSLEngineResult clientResult;
159
160
// results from server's last operation
161
SSLEngineResult serverResult;
162
163
/*
164
* Examining the SSLEngineResults could be much more involved,
165
* and may alter the overall flow of the application.
166
*
167
* For example, if we received a BUFFER_OVERFLOW when trying
168
* to write to the output pipe, we could reallocate a larger
169
* pipe, but instead we wait for the peer to drain it.
170
*/
171
Exception clientException = null;
172
Exception serverException = null;
173
174
while (!dataDone) {
175
log("================");
176
177
try {
178
clientResult = clientEngine.wrap(clientOut, cTOs);
179
log("client wrap: ", clientResult);
180
} catch (Exception e) {
181
clientException = e;
182
System.err.println("Client wrap() threw: " + e.getMessage());
183
}
184
logEngineStatus(clientEngine);
185
runDelegatedTasks(clientEngine);
186
187
log("----");
188
189
try {
190
serverResult = serverEngine.wrap(serverOut, sTOc);
191
log("server wrap: ", serverResult);
192
} catch (Exception e) {
193
serverException = e;
194
System.err.println("Server wrap() threw: " + e.getMessage());
195
}
196
logEngineStatus(serverEngine);
197
runDelegatedTasks(serverEngine);
198
199
cTOs.flip();
200
sTOc.flip();
201
202
log("--------");
203
204
try {
205
clientResult = clientEngine.unwrap(sTOc, clientIn);
206
log("client unwrap: ", clientResult);
207
} catch (Exception e) {
208
clientException = e;
209
System.err.println("Client unwrap() threw: " + e.getMessage());
210
}
211
logEngineStatus(clientEngine);
212
runDelegatedTasks(clientEngine);
213
214
log("----");
215
216
try {
217
serverResult = serverEngine.unwrap(cTOs, serverIn);
218
log("server unwrap: ", serverResult);
219
} catch (Exception e) {
220
serverException = e;
221
System.err.println("Server unwrap() threw: " + e.getMessage());
222
}
223
logEngineStatus(serverEngine);
224
runDelegatedTasks(serverEngine);
225
226
cTOs.compact();
227
sTOc.compact();
228
229
/*
230
* After we've transfered all application data between the client
231
* and server, we close the clientEngine's outbound stream.
232
* This generates a close_notify handshake message, which the
233
* server engine receives and responds by closing itself.
234
*/
235
if (!dataDone && (clientOut.limit() == serverIn.position()) &&
236
(serverOut.limit() == clientIn.position())) {
237
238
/*
239
* A sanity check to ensure we got what was sent.
240
*/
241
checkTransfer(serverOut, clientIn);
242
checkTransfer(clientOut, serverIn);
243
244
dataDone = true;
245
}
246
}
247
}
248
249
/**
250
* The goal of this function is to start a simple TLS session resumption
251
* and get the client hello message data back so it can be inspected.
252
*
253
* @param clientEngine
254
*
255
* @return a ByteBuffer consisting of the ClientHello TLS record.
256
*
257
* @throws Exception if any processing goes wrong.
258
*/
259
private static ByteBuffer getResumptionClientHello(SSLEngine clientEngine)
260
throws Exception {
261
// Create all the buffers
262
SSLSession session = clientEngine.getSession();
263
int appBufferMax = session.getApplicationBufferSize();
264
int netBufferMax = session.getPacketBufferSize();
265
ByteBuffer cTOs = ByteBuffer.allocateDirect(netBufferMax);
266
Exception clientException = null;
267
268
// results from client's last operation
269
SSLEngineResult clientResult;
270
271
// results from server's last operation
272
SSLEngineResult serverResult;
273
274
log("================");
275
276
// Start by having the client create a new ClientHello. It should
277
// contain PSK info that allows it to attempt session resumption.
278
try {
279
clientResult = clientEngine.wrap(clientOut, cTOs);
280
log("client wrap: ", clientResult);
281
} catch (Exception e) {
282
clientException = e;
283
System.err.println("Client wrap() threw: " + e.getMessage());
284
}
285
logEngineStatus(clientEngine);
286
runDelegatedTasks(clientEngine);
287
288
log("----");
289
290
cTOs.flip();
291
return cTOs;
292
}
293
294
/**
295
* This method walks a ClientHello TLS record, looking for a matching
296
* server_name hostname value from the original handshake and a PSK
297
* extension, which indicates (in the context of this test) that this
298
* is a resumed handshake.
299
*
300
* @param resCliHello a ByteBuffer consisting of a complete TLS handshake
301
* record that is a ClientHello message. The position of the buffer
302
* must be at the beginning of the TLS record header.
303
*
304
* @throws Exception if any of the consistency checks for the TLS record,
305
* or handshake message fails. It will also throw an exception if
306
* either the server_name extension doesn't have a matching hostname
307
* field or the pre_shared_key extension is not present.
308
*/
309
private static void checkResumedClientHelloSNI(ByteBuffer resCliHello)
310
throws Exception {
311
boolean foundMatchingSNI = false;
312
boolean foundPSK = false;
313
314
// Advance past the following fields:
315
// TLS Record header (5 bytes)
316
resCliHello.position(resCliHello.position() + 5);
317
318
// Get the next byte and make sure it is a Client Hello
319
byte hsMsgType = resCliHello.get();
320
if (hsMsgType != 0x01) {
321
throw new Exception("Message is not a ClientHello, MsgType = " +
322
hsMsgType);
323
}
324
325
// Skip past the length (3 bytes)
326
resCliHello.position(resCliHello.position() + 3);
327
328
// Protocol version should be TLSv1.2 (0x03, 0x03)
329
short chProto = resCliHello.getShort();
330
if (chProto != 0x0303) {
331
throw new Exception(
332
"Client Hello protocol version is not TLSv1.2: Got " +
333
String.format("0x%04X", chProto));
334
}
335
336
// Skip 32-bytes of random data
337
resCliHello.position(resCliHello.position() + 32);
338
339
// Get the legacy session length and skip that many bytes
340
int sessIdLen = Byte.toUnsignedInt(resCliHello.get());
341
resCliHello.position(resCliHello.position() + sessIdLen);
342
343
// Skip over all the cipher suites
344
int csLen = Short.toUnsignedInt(resCliHello.getShort());
345
resCliHello.position(resCliHello.position() + csLen);
346
347
// Skip compression methods
348
int compLen = Byte.toUnsignedInt(resCliHello.get());
349
resCliHello.position(resCliHello.position() + compLen);
350
351
// Parse the extensions. Get length first, then walk the extensions
352
// List and look for the presence of the PSK extension and server_name.
353
// For server_name, make sure it is the same as what was provided
354
// in the original handshake.
355
System.err.println("ClientHello Extensions Check");
356
int extListLen = Short.toUnsignedInt(resCliHello.getShort());
357
while (extListLen > 0) {
358
// Get the Extension type and length
359
int extType = Short.toUnsignedInt(resCliHello.getShort());
360
int extLen = Short.toUnsignedInt(resCliHello.getShort());
361
switch (extType) {
362
case 0: // server_name
363
System.err.println("* Found server_name Extension");
364
int snListLen = Short.toUnsignedInt(resCliHello.getShort());
365
while (snListLen > 0) {
366
int nameType = Byte.toUnsignedInt(resCliHello.get());
367
if (nameType == 0) { // host_name
368
int hostNameLen =
369
Short.toUnsignedInt(resCliHello.getShort());
370
byte[] hostNameData = new byte[hostNameLen];
371
resCliHello.get(hostNameData);
372
String hostNameStr = new String(hostNameData);
373
System.err.println("\tHostname: " + hostNameStr);
374
if (hostNameStr.equals(HOST_NAME)) {
375
foundMatchingSNI = true;
376
}
377
snListLen -= 3 + hostNameLen; // type, len, data
378
} else { // something else
379
// We don't support anything else and cannot
380
// know how to advance. Throw an exception
381
throw new Exception("Unknown server name type: " +
382
nameType);
383
}
384
}
385
break;
386
case 41: // pre_shared_key
387
// We're not going to bother checking the value. The
388
// presence of the extension in the context of this test
389
// is good enough to tell us this is a resumed ClientHello.
390
foundPSK = true;
391
System.err.println("* Found pre_shared_key Extension");
392
resCliHello.position(resCliHello.position() + extLen);
393
break;
394
default:
395
System.err.format("* Found extension %d (%d bytes)\n",
396
extType, extLen);
397
resCliHello.position(resCliHello.position() + extLen);
398
break;
399
}
400
extListLen -= extLen + 4; // Ext type(2), length(2), data(var.)
401
}
402
403
// At the end of all the extension processing, either we've found
404
// both extensions and the server_name matches our expected value
405
// or we throw an exception.
406
if (!foundMatchingSNI) {
407
throw new Exception("Could not find a matching server_name");
408
} else if (!foundPSK) {
409
throw new Exception("Missing PSK extension, not a resumption?");
410
}
411
}
412
413
/**
414
* Create a TrustManagerFactory from a given keystore.
415
*
416
* @param tsPath the path to the trust store file.
417
* @param pass the password for the trust store.
418
*
419
* @return a new TrustManagerFactory built from the trust store provided.
420
*
421
* @throws GeneralSecurityException if any processing errors occur
422
* with the Keystore instantiation or TrustManagerFactory creation.
423
* @throws IOException if any loading error with the trust store occurs.
424
*/
425
private static TrustManagerFactory makeTrustManagerFactory(String tsPath,
426
char[] pass) throws GeneralSecurityException, IOException {
427
TrustManagerFactory tmf;
428
KeyStore ts = KeyStore.getInstance("JKS");
429
430
try (FileInputStream fsIn = new FileInputStream(tsPath)) {
431
ts.load(fsIn, pass);
432
tmf = TrustManagerFactory.getInstance("SunX509");
433
tmf.init(ts);
434
}
435
return tmf;
436
}
437
438
/**
439
* Create a KeyManagerFactory from a given keystore.
440
*
441
* @param ksPath the path to the keystore file.
442
* @param pass the password for the keystore.
443
*
444
* @return a new TrustManagerFactory built from the keystore provided.
445
*
446
* @throws GeneralSecurityException if any processing errors occur
447
* with the Keystore instantiation or KeyManagerFactory creation.
448
* @throws IOException if any loading error with the keystore occurs
449
*/
450
private static KeyManagerFactory makeKeyManagerFactory(String ksPath,
451
char[] pass) throws GeneralSecurityException, IOException {
452
KeyManagerFactory kmf;
453
KeyStore ks = KeyStore.getInstance("JKS");
454
455
try (FileInputStream fsIn = new FileInputStream(ksPath)) {
456
ks.load(fsIn, pass);
457
kmf = KeyManagerFactory.getInstance("SunX509");
458
kmf.init(ks, pass);
459
}
460
return kmf;
461
}
462
463
/**
464
* Create an SSLEngine instance from a given protocol specifier,
465
* KeyManagerFactory and TrustManagerFactory.
466
*
467
* @param ctx the SSLContext used to create the SSLEngine
468
* @param kmf an initialized KeyManagerFactory. May be null.
469
* @param tmf an initialized TrustManagerFactory. May be null.
470
* @param isClient true if it intended to create a client engine, false
471
* for a server engine.
472
*
473
* @return an SSLEngine instance configured as a server and with client
474
* authentication disabled.
475
*
476
* @throws GeneralSecurityException if any errors occur during the
477
* creation of the SSLEngine.
478
*/
479
private static SSLEngine makeEngine(SSLContext ctx,
480
KeyManagerFactory kmf, TrustManagerFactory tmf, boolean isClient)
481
throws GeneralSecurityException {
482
ctx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
483
SSLEngine ssle = ctx.createSSLEngine("localhost", 8443);
484
ssle.setUseClientMode(isClient);
485
ssle.setNeedClientAuth(false);
486
return ssle;
487
}
488
489
private static void logEngineStatus(SSLEngine engine) {
490
log("\tCurrent HS State " + engine.getHandshakeStatus().toString());
491
log("\tisInboundDone(): " + engine.isInboundDone());
492
log("\tisOutboundDone(): " + engine.isOutboundDone());
493
}
494
495
/*
496
* If the result indicates that we have outstanding tasks to do,
497
* go ahead and run them in this thread.
498
*/
499
private static void runDelegatedTasks(SSLEngine engine) throws Exception {
500
501
if (engine.getHandshakeStatus() == HandshakeStatus.NEED_TASK) {
502
Runnable runnable;
503
while ((runnable = engine.getDelegatedTask()) != null) {
504
log(" running delegated task...");
505
runnable.run();
506
}
507
HandshakeStatus hsStatus = engine.getHandshakeStatus();
508
if (hsStatus == HandshakeStatus.NEED_TASK) {
509
throw new Exception(
510
"handshake shouldn't need additional tasks");
511
}
512
logEngineStatus(engine);
513
}
514
}
515
516
private static boolean isEngineClosed(SSLEngine engine) {
517
return (engine.isOutboundDone() && engine.isInboundDone());
518
}
519
520
/*
521
* Simple check to make sure everything came across as expected.
522
*/
523
private static void checkTransfer(ByteBuffer a, ByteBuffer b)
524
throws Exception {
525
a.flip();
526
b.flip();
527
528
if (!a.equals(b)) {
529
throw new Exception("Data didn't transfer cleanly");
530
} else {
531
log("\tData transferred cleanly");
532
}
533
534
a.position(a.limit());
535
b.position(b.limit());
536
a.limit(a.capacity());
537
b.limit(b.capacity());
538
}
539
540
/*
541
* Logging code
542
*/
543
private static boolean resultOnce = true;
544
545
private static void log(String str, SSLEngineResult result) {
546
if (!logging) {
547
return;
548
}
549
if (resultOnce) {
550
resultOnce = false;
551
System.err.println("The format of the SSLEngineResult is: \n" +
552
"\t\"getStatus() / getHandshakeStatus()\" +\n" +
553
"\t\"bytesConsumed() / bytesProduced()\"\n");
554
}
555
HandshakeStatus hsStatus = result.getHandshakeStatus();
556
log(str +
557
result.getStatus() + "/" + hsStatus + ", " +
558
result.bytesConsumed() + "/" + result.bytesProduced() +
559
" bytes");
560
if (hsStatus == HandshakeStatus.FINISHED) {
561
log("\t...ready for application data");
562
}
563
}
564
565
private static void log(String str) {
566
if (logging) {
567
System.err.println(str);
568
}
569
}
570
571
private static void dumpBuffer(String header, ByteBuffer data) {
572
data.mark();
573
System.err.format("========== %s ==========\n", header);
574
int i = 0;
575
while (data.remaining() > 0) {
576
if (i != 0 && i % 16 == 0) {
577
System.err.print("\n");
578
}
579
System.err.format("%02X ", data.get());
580
i++;
581
}
582
System.err.println();
583
data.reset();
584
}
585
586
}
587
588