1 /* 2 * Copyright (c) 2016, 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 // See ../../../../RunStatReqSelect.java for the jtreg header 28 29 package sun.security.ssl; 30 31 import javax.net.ssl.*; 32 import javax.net.ssl.SSLEngineResult.*; 33 import javax.security.auth.x500.X500Principal; 34 import java.io.*; 35 import java.math.BigInteger; 36 import java.security.*; 37 import java.nio.*; 38 import java.security.cert.X509Certificate; 39 import java.security.cert.Extension; 40 import java.util.ArrayList; 41 import java.util.Collections; 42 import java.util.Date; 43 import java.util.HashMap; 44 import java.util.List; 45 import java.util.Map; 46 import java.util.Objects; 47 import java.util.concurrent.TimeUnit; 48 49 import sun.security.provider.certpath.OCSPNonceExtension; 50 import sun.security.provider.certpath.ResponderId; 51 import sun.security.testlibrary.SimpleOCSPServer; 52 import sun.security.testlibrary.CertificateBuilder; 53 54 public class StatusReqSelection { 55 56 /* 57 * Enables logging of the SSLEngine operations. 58 */ 59 private static final boolean logging = true; 60 61 /* 62 * Enables the JSSE system debugging system property: 63 * 64 * -Djavax.net.debug=all 65 * 66 * This gives a lot of low-level information about operations underway, 67 * including specific handshake messages, and might be best examined 68 * after gaining some familiarity with this application. 69 */ 70 private static final boolean debug = false; 71 72 // The following items are used to set up the keystores. 73 private static final String passwd = "passphrase"; 74 private static final String ROOT_ALIAS = "root"; 75 private static final String INT_ALIAS = "intermediate"; 76 private static final String SSL_ALIAS = "ssl"; 77 78 // PKI and server components we will need for this test 79 private static KeyManagerFactory kmf; 80 private static TrustManagerFactory tmf; 81 private static KeyStore rootKeystore; // Root CA Keystore 82 private static KeyStore intKeystore; // Intermediate CA Keystore 83 private static KeyStore serverKeystore; // SSL Server Keystore 84 private static KeyStore trustStore; // SSL Client trust store 85 private static SimpleOCSPServer rootOcsp; // Root CA OCSP Responder 86 private static int rootOcspPort; // Port for root OCSP 87 private static SimpleOCSPServer intOcsp; // Intermediate CA OCSP server 88 private static int intOcspPort; // Port for intermediate OCSP 89 private static SSLContext ctxStaple; // SSLContext for all tests 90 91 // Some useful objects we will need for test purposes 92 private static final SecureRandom RNG = new SecureRandom(); 93 94 // We'll be using these objects repeatedly to make hello messages 95 private static final ProtocolVersion VER_1_0 = ProtocolVersion.TLS10; 96 private static final ProtocolVersion VER_1_2 = ProtocolVersion.TLS12; 97 private static final CipherSuiteList SUITES = new CipherSuiteList( 98 CipherSuite.valueOf("TLS_RSA_WITH_AES_128_GCM_SHA256")); 99 private static final SessionId SID = new SessionId(new byte[0]); 100 private static final HelloExtension RNIEXT = 101 new RenegotiationInfoExtension(new byte[0], new byte[0]); 102 private static final List<SignatureAndHashAlgorithm> algList = 103 new ArrayList<SignatureAndHashAlgorithm>() {{ 104 add(SignatureAndHashAlgorithm.valueOf(4, 1, 0)); 105 }}; // List with only SHA256withRSA 106 private static final SignatureAlgorithmsExtension SIGALGEXT = 107 new SignatureAlgorithmsExtension(algList); 108 109 /* 110 * Main entry point for this test. 111 */ 112 public static void main(String args[]) throws Exception { 113 int testsPassed = 0; 114 115 if (debug) { 116 System.setProperty("javax.net.debug", "ssl"); 117 } 118 119 // All tests will have stapling enabled on the server side 120 System.setProperty("jdk.tls.server.enableStatusRequestExtension", 121 "true"); 122 123 // Create a single SSLContext that we can use for all tests 124 ctxStaple = SSLContext.getInstance("TLS"); 125 126 // Create the PKI we will use for the test and start the OCSP servers 127 createPKI(); 128 129 // Set up the KeyManagerFactory and TrustManagerFactory 130 kmf = KeyManagerFactory.getInstance("PKIX"); 131 kmf.init(serverKeystore, passwd.toCharArray()); 132 tmf = TrustManagerFactory.getInstance("PKIX"); 133 tmf.init(trustStore); 134 135 List<TestCase> testList = new ArrayList<TestCase>() {{ 136 add(new TestCase("ClientHello: No stapling extensions", 137 makeHelloNoStaplingExts(), false, false)); 138 add(new TestCase("ClientHello: Default status_request only", 139 makeDefaultStatReqOnly(), true, false)); 140 add(new TestCase("ClientHello: Default status_request_v2 only", 141 makeDefaultStatReqV2Only(), false, true)); 142 add(new TestCase("ClientHello: Both status_request exts, default", 143 makeDefaultStatReqBoth(), false, true)); 144 add(new TestCase( 145 "ClientHello: Hello with status_request and responder IDs", 146 makeStatReqWithRid(), false, false)); 147 add(new TestCase( 148 "ClientHello: Hello with status_request using no " + 149 "responder IDs but provides the OCSP nonce extension", 150 makeStatReqNoRidNonce(), true, false)); 151 add(new TestCase("ClientHello with default status_request and " + 152 "status_request_v2 with ResponderIds", 153 makeStatReqDefV2WithRid(), true, false)); 154 add(new TestCase("ClientHello with default status_request and " + 155 "status_request_v2 (OCSP_MULTI with ResponderId, " + 156 "OCSP as a default request)", 157 makeStatReqDefV2MultiWithRidSingleDef(), false, true)); 158 add(new TestCase("ClientHello with status_request and " + 159 "status_request_v2 and all OCSPStatusRequests use " + 160 "Responder IDs", 161 makeStatReqAllWithRid(), false, false)); 162 add(new TestCase("ClientHello with default status_request and " + 163 "status_request_v2 that has a default OCSP item and " + 164 "multiple OCSP_MULTI items, only one is default", 165 makeHelloMultiV2andSingle(), false, true)); 166 }}; 167 168 // Run the client and server property tests 169 for (TestCase test : testList) { 170 try { 171 log("*** Test: " + test.testName); 172 if (runTest(test)) { 173 log("PASS: status_request: " + test.statReqEnabled + 174 ", status_request_v2: " + test.statReqV2Enabled); 175 testsPassed++; 176 } 177 } catch (Exception e) { 178 // If we get an exception, we'll count it as a failure 179 log("Test failure due to exception: " + e); 180 } 181 log(""); 182 } 183 184 // Summary 185 if (testsPassed != testList.size()) { 186 throw new RuntimeException(testList.size() - testsPassed + 187 " tests failed out of " + testList.size() + " total."); 188 } else { 189 log("Total tests: " + testList.size() + ", all passed"); 190 } 191 } 192 193 private static boolean runTest(TestCase test) throws Exception { 194 SSLEngineResult serverResult; 195 196 // Create a Server SSLEngine to receive our customized ClientHello 197 ctxStaple.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null); 198 SSLEngine engine = ctxStaple.createSSLEngine(); 199 engine.setUseClientMode(false); 200 engine.setNeedClientAuth(false); 201 202 SSLSession session = engine.getSession(); 203 ByteBuffer serverOut = ByteBuffer.wrap("I'm a Server".getBytes()); 204 ByteBuffer serverIn = 205 ByteBuffer.allocate(session.getApplicationBufferSize() + 50); 206 ByteBuffer sTOc = 207 ByteBuffer.allocateDirect(session.getPacketBufferSize()); 208 209 // Send the ClientHello ByteBuffer in the test case 210 if (debug) { 211 System.out.println("Sending Client Hello:\n" + 212 dumpHexBytes(test.data)); 213 } 214 215 // Consume the client hello 216 serverResult = engine.unwrap(test.data, serverIn); 217 if (debug) { 218 log("server unwrap: ", serverResult); 219 } 220 if (serverResult.getStatus() != SSLEngineResult.Status.OK) { 221 throw new SSLException("Server unwrap got status: " + 222 serverResult.getStatus()); 223 } else if (serverResult.getHandshakeStatus() != 224 SSLEngineResult.HandshakeStatus.NEED_TASK) { 225 throw new SSLException("Server unwrap expected NEED_TASK, got: " + 226 serverResult.getHandshakeStatus()); 227 } 228 runDelegatedTasks(serverResult, engine); 229 if (engine.getHandshakeStatus() != 230 SSLEngineResult.HandshakeStatus.NEED_WRAP) { 231 throw new SSLException("Expected NEED_WRAP, got: " + 232 engine.getHandshakeStatus()); 233 } 234 235 // Generate a TLS record with the ServerHello 236 serverResult = engine.wrap(serverOut, sTOc); 237 if (debug) { 238 log("client wrap: ", serverResult); 239 } 240 if (serverResult.getStatus() != SSLEngineResult.Status.OK) { 241 throw new SSLException("Client wrap got status: " + 242 serverResult.getStatus()); 243 } 244 sTOc.flip(); 245 246 if (debug) { 247 log("Server Response:\n" + dumpHexBytes(sTOc)); 248 } 249 250 return checkServerHello(sTOc, test.statReqEnabled, 251 test.statReqV2Enabled); 252 } 253 254 /** 255 * Make a TLSv1.2 ClientHello with only RNI and no stapling extensions 256 */ 257 private static ByteBuffer makeHelloNoStaplingExts() throws IOException { 258 // Craft the ClientHello byte buffer 259 HelloExtensions exts = new HelloExtensions(); 260 exts.add(RNIEXT); 261 exts.add(SIGALGEXT); 262 return createTlsRecord(Record.ct_handshake, VER_1_2, 263 createClientHelloMsg(VER_1_2, SID, SUITES, exts)); 264 } 265 266 /** 267 * Make a TLSv1.2 ClientHello with the RNI and Status Request extensions 268 */ 269 private static ByteBuffer makeDefaultStatReqOnly() throws IOException { 270 // Craft the ClientHello byte buffer 271 HelloExtensions exts = new HelloExtensions(); 272 exts.add(RNIEXT); 273 exts.add(SIGALGEXT); 274 exts.add(new CertStatusReqExtension(StatusRequestType.OCSP, 275 new OCSPStatusRequest(null, null))); 276 return createTlsRecord(Record.ct_handshake, VER_1_2, 277 createClientHelloMsg(VER_1_2, SID, SUITES, exts)); 278 } 279 280 /** 281 * Make a TLSv1.2 ClientHello with the RNI and Status Request V2 extension 282 */ 283 private static ByteBuffer makeDefaultStatReqV2Only() throws IOException { 284 // Craft the ClientHello byte buffer 285 HelloExtensions exts = new HelloExtensions(); 286 OCSPStatusRequest osr = new OCSPStatusRequest(); 287 List<CertStatusReqItemV2> itemList = new ArrayList<>(2); 288 itemList.add(new CertStatusReqItemV2(StatusRequestType.OCSP_MULTI, 289 osr)); 290 itemList.add(new CertStatusReqItemV2(StatusRequestType.OCSP, osr)); 291 292 exts.add(RNIEXT); 293 exts.add(SIGALGEXT); 294 exts.add(new CertStatusReqListV2Extension(itemList)); 295 return createTlsRecord(Record.ct_handshake, VER_1_2, 296 createClientHelloMsg(VER_1_2, SID, SUITES, exts)); 297 } 298 /** 299 * Make a TLSv1.2 ClientHello with Status Request and Status Request V2 300 * extensions. 301 */ 302 private static ByteBuffer makeDefaultStatReqBoth() throws IOException { 303 // Craft the ClientHello byte buffer 304 HelloExtensions exts = new HelloExtensions(); 305 OCSPStatusRequest osr = new OCSPStatusRequest(); 306 List<CertStatusReqItemV2> itemList = new ArrayList<>(2); 307 itemList.add(new CertStatusReqItemV2(StatusRequestType.OCSP_MULTI, 308 osr)); 309 itemList.add(new CertStatusReqItemV2(StatusRequestType.OCSP, osr)); 310 311 exts.add(RNIEXT); 312 exts.add(SIGALGEXT); 313 exts.add(new CertStatusReqExtension(StatusRequestType.OCSP, 314 new OCSPStatusRequest(null, null))); 315 exts.add(new CertStatusReqListV2Extension(itemList)); 316 return createTlsRecord(Record.ct_handshake, VER_1_2, 317 createClientHelloMsg(VER_1_2, SID, SUITES, exts)); 318 } 319 320 /** 321 * Make a ClientHello using a status_request that has a single 322 * responder ID in it. 323 */ 324 private static ByteBuffer makeStatReqWithRid() throws IOException { 325 HelloExtensions exts = new HelloExtensions(); 326 exts.add(RNIEXT); 327 exts.add(SIGALGEXT); 328 List<ResponderId> rids = new ArrayList<ResponderId>() {{ 329 add(new ResponderId(new X500Principal("CN=Foo"))); 330 }}; 331 exts.add(new CertStatusReqExtension(StatusRequestType.OCSP, 332 new OCSPStatusRequest(rids, null))); 333 return createTlsRecord(Record.ct_handshake, VER_1_2, 334 createClientHelloMsg(VER_1_2, SID, SUITES, exts)); 335 } 336 337 /** 338 * Make a ClientHello using a status_request that has no 339 * responder IDs but does provide the nonce extension. 340 */ 341 private static ByteBuffer makeStatReqNoRidNonce() throws IOException { 342 HelloExtensions exts = new HelloExtensions(); 343 exts.add(RNIEXT); 344 exts.add(SIGALGEXT); 345 List<Extension> ocspExts = new ArrayList<Extension>() {{ 346 add(new OCSPNonceExtension(16)); 347 }}; 348 exts.add(new CertStatusReqExtension(StatusRequestType.OCSP, 349 new OCSPStatusRequest(null, ocspExts))); 350 return createTlsRecord(Record.ct_handshake, VER_1_2, 351 createClientHelloMsg(VER_1_2, SID, SUITES, exts)); 352 } 353 354 /** 355 * Make a ClientHello using a default status_request and a 356 * status_request_v2 that has a single responder ID in it. 357 */ 358 private static ByteBuffer makeStatReqDefV2WithRid() throws IOException { 359 HelloExtensions exts = new HelloExtensions(); 360 List<ResponderId> rids = new ArrayList<ResponderId>() {{ 361 add(new ResponderId(new X500Principal("CN=Foo"))); 362 }}; 363 List<CertStatusReqItemV2> itemList = new ArrayList<>(2); 364 itemList.add(new CertStatusReqItemV2(StatusRequestType.OCSP_MULTI, 365 new OCSPStatusRequest(rids, null))); 366 itemList.add(new CertStatusReqItemV2(StatusRequestType.OCSP, 367 new OCSPStatusRequest(rids, null))); 368 369 exts.add(RNIEXT); 370 exts.add(SIGALGEXT); 371 exts.add(new CertStatusReqExtension(StatusRequestType.OCSP, 372 new OCSPStatusRequest(null, null))); 373 exts.add(new CertStatusReqListV2Extension(itemList)); 374 return createTlsRecord(Record.ct_handshake, VER_1_2, 375 createClientHelloMsg(VER_1_2, SID, SUITES, exts)); 376 } 377 378 /** 379 * Make a ClientHello using a default status_request and a 380 * status_request_v2 that has a single responder ID in it for the 381 * OCSP_MULTI request item and a default OCSP request item. 382 */ 383 private static ByteBuffer makeStatReqDefV2MultiWithRidSingleDef() 384 throws IOException { 385 HelloExtensions exts = new HelloExtensions(); 386 List<ResponderId> rids = new ArrayList<ResponderId>() {{ 387 add(new ResponderId(new X500Principal("CN=Foo"))); 388 }}; 389 List<CertStatusReqItemV2> itemList = new ArrayList<>(2); 390 itemList.add(new CertStatusReqItemV2(StatusRequestType.OCSP_MULTI, 391 new OCSPStatusRequest(rids, null))); 392 itemList.add(new CertStatusReqItemV2(StatusRequestType.OCSP, 393 new OCSPStatusRequest(null, null))); 394 395 exts.add(RNIEXT); 396 exts.add(SIGALGEXT); 397 exts.add(new CertStatusReqExtension(StatusRequestType.OCSP, 398 new OCSPStatusRequest(null, null))); 399 exts.add(new CertStatusReqListV2Extension(itemList)); 400 return createTlsRecord(Record.ct_handshake, VER_1_2, 401 createClientHelloMsg(VER_1_2, SID, SUITES, exts)); 402 } 403 404 /** 405 * Make a ClientHello using status_request and status_request_v2 where 406 * all underlying OCSPStatusRequests use responder IDs. 407 */ 408 private static ByteBuffer makeStatReqAllWithRid() throws IOException { 409 HelloExtensions exts = new HelloExtensions(); 410 List<ResponderId> rids = new ArrayList<ResponderId>() {{ 411 add(new ResponderId(new X500Principal("CN=Foo"))); 412 }}; 413 List<CertStatusReqItemV2> itemList = new ArrayList<>(2); 414 itemList.add(new CertStatusReqItemV2(StatusRequestType.OCSP_MULTI, 415 new OCSPStatusRequest(rids, null))); 416 itemList.add(new CertStatusReqItemV2(StatusRequestType.OCSP, 417 new OCSPStatusRequest(rids, null))); 418 419 exts.add(RNIEXT); 420 exts.add(SIGALGEXT); 421 exts.add(new CertStatusReqExtension(StatusRequestType.OCSP, 422 new OCSPStatusRequest(rids, null))); 423 exts.add(new CertStatusReqListV2Extension(itemList)); 424 return createTlsRecord(Record.ct_handshake, VER_1_2, 425 createClientHelloMsg(VER_1_2, SID, SUITES, exts)); 426 } 427 428 /** 429 * Make a TLSv1.2 ClientHello multiple CertStatusReqItemV2s of different 430 * types. One of the middle items should be acceptable while the others 431 * have responder IDs. The status_request (v1) should also be acceptable 432 * but should be overridden in favor of the status_request_v2. 433 */ 434 private static ByteBuffer makeHelloMultiV2andSingle() throws IOException { 435 // Craft the ClientHello byte buffer 436 HelloExtensions exts = new HelloExtensions(); 437 List<ResponderId> fooRid = Collections.singletonList( 438 new ResponderId(new X500Principal("CN=Foo"))); 439 List<ResponderId> barRid = Collections.singletonList( 440 new ResponderId(new X500Principal("CN=Bar"))); 441 List<CertStatusReqItemV2> itemList = new ArrayList<>(); 442 itemList.add(new CertStatusReqItemV2(StatusRequestType.OCSP, 443 new OCSPStatusRequest(null, null))); 444 itemList.add(new CertStatusReqItemV2(StatusRequestType.OCSP_MULTI, 445 new OCSPStatusRequest(fooRid, null))); 446 itemList.add(new CertStatusReqItemV2(StatusRequestType.OCSP_MULTI, 447 new OCSPStatusRequest(null, null))); 448 itemList.add(new CertStatusReqItemV2(StatusRequestType.OCSP_MULTI, 449 new OCSPStatusRequest(barRid, null))); 450 451 exts.add(RNIEXT); 452 exts.add(SIGALGEXT); 453 exts.add(new CertStatusReqExtension(StatusRequestType.OCSP, 454 new OCSPStatusRequest(null, null))); 455 exts.add(new CertStatusReqListV2Extension(itemList)); 456 return createTlsRecord(Record.ct_handshake, VER_1_2, 457 createClientHelloMsg(VER_1_2, SID, SUITES, exts)); 458 } 459 460 /** 461 * Wrap a TLS content message into a TLS record header 462 * 463 * @param contentType a byte containing the content type value 464 * @param pv the protocol version for this record 465 * @param data a byte buffer containing the message data 466 * @return 467 */ 468 private static ByteBuffer createTlsRecord(byte contentType, 469 ProtocolVersion pv, ByteBuffer data) { 470 int msgLen = (data != null) ? data.limit() : 0; 471 472 // Allocate enough space to hold the TLS record header + the message 473 ByteBuffer recordBuf = ByteBuffer.allocate(msgLen + 5); 474 recordBuf.put(contentType); 475 recordBuf.putShort((short)pv.v); 476 recordBuf.putShort((short)msgLen); 477 if (msgLen > 0) { 478 recordBuf.put(data); 479 } 480 481 recordBuf.flip(); 482 return recordBuf; 483 } 484 485 /** 486 * Craft and encode a ClientHello message as a byte array. 487 * 488 * @param pv the protocol version asserted in the hello message. 489 * @param sessId the session ID for this hello message. 490 * @param suites a list consisting of one or more cipher suite objects 491 * @param extensions a list of HelloExtension objects 492 * 493 * @return a byte array containing the encoded ClientHello message. 494 */ 495 private static ByteBuffer createClientHelloMsg(ProtocolVersion pv, 496 SessionId sessId, CipherSuiteList suites, 497 HelloExtensions extensions) throws IOException { 498 ByteBuffer msgBuf; 499 500 HandshakeOutStream hsos = 501 new HandshakeOutStream(new SSLEngineOutputRecord()); 502 503 // Construct the client hello object from the first 3 parameters 504 HandshakeMessage.ClientHello cHello = 505 new HandshakeMessage.ClientHello(RNG, pv, sessId, suites, 506 false); 507 508 // Use the HelloExtensions provided by the caller 509 if (extensions != null) { 510 cHello.extensions = extensions; 511 } 512 513 cHello.send(hsos); 514 msgBuf = ByteBuffer.allocate(hsos.size() + 4); 515 516 // Combine the handshake type with the length 517 msgBuf.putInt((HandshakeMessage.ht_client_hello << 24) | 518 (hsos.size() & 0x00FFFFFF)); 519 msgBuf.put(hsos.toByteArray()); 520 msgBuf.flip(); 521 return msgBuf; 522 } 523 524 /* 525 * If the result indicates that we have outstanding tasks to do, 526 * go ahead and run them in this thread. 527 */ 528 private static void runDelegatedTasks(SSLEngineResult result, 529 SSLEngine engine) throws Exception { 530 531 if (result.getHandshakeStatus() == HandshakeStatus.NEED_TASK) { 532 Runnable runnable; 533 while ((runnable = engine.getDelegatedTask()) != null) { 534 if (debug) { 535 log("\trunning delegated task..."); 536 } 537 runnable.run(); 538 } 539 HandshakeStatus hsStatus = engine.getHandshakeStatus(); 540 if (hsStatus == HandshakeStatus.NEED_TASK) { 541 throw new Exception( 542 "handshake shouldn't need additional tasks"); 543 } 544 if (debug) { 545 log("\tnew HandshakeStatus: " + hsStatus); 546 } 547 } 548 } 549 550 private static void log(String str, SSLEngineResult result) { 551 if (!logging) { 552 return; 553 } 554 HandshakeStatus hsStatus = result.getHandshakeStatus(); 555 log(str + 556 result.getStatus() + "/" + hsStatus + ", " + 557 result.bytesConsumed() + "/" + result.bytesProduced() + 558 " bytes"); 559 if (hsStatus == HandshakeStatus.FINISHED) { 560 log("\t...ready for application data"); 561 } 562 } 563 564 private static void log(String str) { 565 if (logging) { 566 System.out.println(str); 567 } 568 } 569 570 /** 571 * Dump a ByteBuffer as a hexdump to stdout. The dumping routine will 572 * start at the current position of the buffer and run to its limit. 573 * After completing the dump, the position will be returned to its 574 * starting point. 575 * 576 * @param data the ByteBuffer to dump to stdout. 577 * 578 * @return the hexdump of the byte array. 579 */ 580 private static String dumpHexBytes(ByteBuffer data) { 581 StringBuilder sb = new StringBuilder(); 582 if (data != null) { 583 int i = 0; 584 data.mark(); 585 while (data.hasRemaining()) { 586 if (i % 16 == 0 && i != 0) { 587 sb.append("\n"); 588 } 589 sb.append(String.format("%02X ", data.get())); 590 i++; 591 } 592 data.reset(); 593 } 594 595 return sb.toString(); 596 } 597 598 /** 599 * Tests the ServerHello for the presence (or not) of the status_request 600 * or status_request_v2 hello extension. It is assumed that the provided 601 * ByteBuffer has its position set at the first byte of the TLS record 602 * containing the ServerHello and contains the entire hello message. Upon 603 * successful completion of this method the ByteBuffer will have its 604 * position reset to the initial offset in the buffer. If an exception is 605 * thrown the position at the time of the exception will be preserved. 606 * 607 * @param statReqPresent true if the status_request hello extension should 608 * be present. 609 * @param statReqV2Present true if the status_request_v2 hello extension 610 * should be present. 611 * 612 * @return true if the ServerHello's extension set matches the presence 613 * booleans for status_request and status_request_v2. False if 614 * not, or if the TLS record or message is of the wrong type. 615 */ 616 private static boolean checkServerHello(ByteBuffer data, 617 boolean statReqPresent, boolean statReqV2Present) { 618 boolean hasV1 = false; 619 boolean hasV2 = false; 620 Objects.requireNonNull(data); 621 int startPos = data.position(); 622 data.mark(); 623 624 // Process the TLS record header 625 int type = Byte.toUnsignedInt(data.get()); 626 int ver_major = Byte.toUnsignedInt(data.get()); 627 int ver_minor = Byte.toUnsignedInt(data.get()); 628 int recLen = Short.toUnsignedInt(data.getShort()); 629 630 // Simple sanity checks 631 if (type != 22) { 632 log("Not a handshake: Type = " + type); 633 return false; 634 } else if (recLen > data.remaining()) { 635 log("Incomplete record in buffer: Record length = " + recLen + 636 ", Remaining = " + data.remaining()); 637 return false; 638 } 639 640 // Grab the handshake message header. 641 int msgHdr = data.getInt(); 642 int msgType = (msgHdr >> 24) & 0x000000FF; 643 int msgLen = msgHdr & 0x00FFFFFF; 644 645 // More simple sanity checks 646 if (msgType != 2) { 647 log("Not a ServerHello: Type = " + msgType); 648 return false; 649 } 650 651 // Skip over the protocol version and server random 652 data.position(data.position() + 34); 653 654 // Jump past the session ID 655 int sessLen = Byte.toUnsignedInt(data.get()); 656 if (sessLen != 0) { 657 data.position(data.position() + sessLen); 658 } 659 660 // Skip the cipher suite and compression method 661 data.position(data.position() + 3); 662 663 // Go through the extensions and look for the request extension 664 // expected by the caller. 665 int extsLen = Short.toUnsignedInt(data.getShort()); 666 while (data.position() < recLen + startPos + 5) { 667 int extType = Short.toUnsignedInt(data.getShort()); 668 int extLen = Short.toUnsignedInt(data.getShort()); 669 hasV1 |= (extType == ExtensionType.EXT_STATUS_REQUEST.id); 670 hasV2 |= (extType == ExtensionType.EXT_STATUS_REQUEST_V2.id); 671 data.position(data.position() + extLen); 672 } 673 674 if (hasV1 != statReqPresent) { 675 log("The status_request extension is " + 676 "inconsistent with the expected result: expected = " + 677 statReqPresent + ", actual = " + hasV1); 678 } 679 if (hasV2 != statReqV2Present) { 680 log("The status_request_v2 extension is " + 681 "inconsistent with the expected result: expected = " + 682 statReqV2Present + ", actual = " + hasV2); 683 } 684 685 // Reset the position to the initial spot at the start of this method. 686 data.reset(); 687 688 return ((hasV1 == statReqPresent) && (hasV2 == statReqV2Present)); 689 } 690 691 /** 692 * Creates the PKI components necessary for this test, including 693 * Root CA, Intermediate CA and SSL server certificates, the keystores 694 * for each entity, a client trust store, and starts the OCSP responders. 695 */ 696 private static void createPKI() throws Exception { 697 CertificateBuilder cbld = new CertificateBuilder(); 698 KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA"); 699 keyGen.initialize(2048); 700 KeyStore.Builder keyStoreBuilder = 701 KeyStore.Builder.newInstance("PKCS12", null, 702 new KeyStore.PasswordProtection(passwd.toCharArray())); 703 704 // Generate Root, IntCA, EE keys 705 KeyPair rootCaKP = keyGen.genKeyPair(); 706 log("Generated Root CA KeyPair"); 707 KeyPair intCaKP = keyGen.genKeyPair(); 708 log("Generated Intermediate CA KeyPair"); 709 KeyPair sslKP = keyGen.genKeyPair(); 710 log("Generated SSL Cert KeyPair"); 711 712 // Set up the Root CA Cert 713 cbld.setSubjectName("CN=Root CA Cert, O=SomeCompany"); 714 cbld.setPublicKey(rootCaKP.getPublic()); 715 cbld.setSerialNumber(new BigInteger("1")); 716 // Make a 3 year validity starting from 60 days ago 717 long start = System.currentTimeMillis() - TimeUnit.DAYS.toMillis(60); 718 long end = start + TimeUnit.DAYS.toMillis(1085); 719 cbld.setValidity(new Date(start), new Date(end)); 720 addCommonExts(cbld, rootCaKP.getPublic(), rootCaKP.getPublic()); 721 addCommonCAExts(cbld); 722 // Make our Root CA Cert! 723 X509Certificate rootCert = cbld.build(null, rootCaKP.getPrivate(), 724 "SHA256withRSA"); 725 log("Root CA Created:\n" + certInfo(rootCert)); 726 727 // Now build a keystore and add the keys and cert 728 rootKeystore = keyStoreBuilder.getKeyStore(); 729 java.security.cert.Certificate[] rootChain = {rootCert}; 730 rootKeystore.setKeyEntry(ROOT_ALIAS, rootCaKP.getPrivate(), 731 passwd.toCharArray(), rootChain); 732 733 // Now fire up the OCSP responder 734 rootOcsp = new SimpleOCSPServer(rootKeystore, passwd, ROOT_ALIAS, null); 735 rootOcsp.enableLog(debug); 736 rootOcsp.setNextUpdateInterval(3600); 737 rootOcsp.start(); 738 739 // Wait 5 seconds for server ready 740 for (int i = 0; (i < 100 && !rootOcsp.isServerReady()); i++) { 741 Thread.sleep(50); 742 } 743 if (!rootOcsp.isServerReady()) { 744 throw new RuntimeException("Server not ready yet"); 745 } 746 747 rootOcspPort = rootOcsp.getPort(); 748 String rootRespURI = "http://localhost:" + rootOcspPort; 749 log("Root OCSP Responder URI is " + rootRespURI); 750 751 // Now that we have the root keystore and OCSP responder we can 752 // create our intermediate CA. 753 cbld.reset(); 754 cbld.setSubjectName("CN=Intermediate CA Cert, O=SomeCompany"); 755 cbld.setPublicKey(intCaKP.getPublic()); 756 cbld.setSerialNumber(new BigInteger("100")); 757 // Make a 2 year validity starting from 30 days ago 758 start = System.currentTimeMillis() - TimeUnit.DAYS.toMillis(30); 759 end = start + TimeUnit.DAYS.toMillis(730); 760 cbld.setValidity(new Date(start), new Date(end)); 761 addCommonExts(cbld, intCaKP.getPublic(), rootCaKP.getPublic()); 762 addCommonCAExts(cbld); 763 cbld.addAIAExt(Collections.singletonList(rootRespURI)); 764 // Make our Intermediate CA Cert! 765 X509Certificate intCaCert = cbld.build(rootCert, rootCaKP.getPrivate(), 766 "SHA256withRSA"); 767 log("Intermediate CA Created:\n" + certInfo(intCaCert)); 768 769 // Provide intermediate CA cert revocation info to the Root CA 770 // OCSP responder. 771 Map<BigInteger, SimpleOCSPServer.CertStatusInfo> revInfo = 772 new HashMap<>(); 773 revInfo.put(intCaCert.getSerialNumber(), 774 new SimpleOCSPServer.CertStatusInfo( 775 SimpleOCSPServer.CertStatus.CERT_STATUS_GOOD)); 776 rootOcsp.updateStatusDb(revInfo); 777 778 // Now build a keystore and add the keys, chain and root cert as a TA 779 intKeystore = keyStoreBuilder.getKeyStore(); 780 java.security.cert.Certificate[] intChain = {intCaCert, rootCert}; 781 intKeystore.setKeyEntry(INT_ALIAS, intCaKP.getPrivate(), 782 passwd.toCharArray(), intChain); 783 intKeystore.setCertificateEntry(ROOT_ALIAS, rootCert); 784 785 // Now fire up the Intermediate CA OCSP responder 786 intOcsp = new SimpleOCSPServer(intKeystore, passwd, 787 INT_ALIAS, null); 788 intOcsp.enableLog(debug); 789 intOcsp.setNextUpdateInterval(3600); 790 intOcsp.start(); 791 792 // Wait 5 seconds for server ready 793 for (int i = 0; (i < 100 && !intOcsp.isServerReady()); i++) { 794 Thread.sleep(50); 795 } 796 if (!intOcsp.isServerReady()) { 797 throw new RuntimeException("Server not ready yet"); 798 } 799 800 intOcspPort = intOcsp.getPort(); 801 String intCaRespURI = "http://localhost:" + intOcspPort; 802 log("Intermediate CA OCSP Responder URI is " + intCaRespURI); 803 804 // Last but not least, let's make our SSLCert and add it to its own 805 // Keystore 806 cbld.reset(); 807 cbld.setSubjectName("CN=SSLCertificate, O=SomeCompany"); 808 cbld.setPublicKey(sslKP.getPublic()); 809 cbld.setSerialNumber(new BigInteger("4096")); 810 // Make a 1 year validity starting from 7 days ago 811 start = System.currentTimeMillis() - TimeUnit.DAYS.toMillis(7); 812 end = start + TimeUnit.DAYS.toMillis(365); 813 cbld.setValidity(new Date(start), new Date(end)); 814 815 // Add extensions 816 addCommonExts(cbld, sslKP.getPublic(), intCaKP.getPublic()); 817 boolean[] kuBits = {true, false, true, false, false, false, 818 false, false, false}; 819 cbld.addKeyUsageExt(kuBits); 820 List<String> ekuOids = new ArrayList<>(); 821 ekuOids.add("1.3.6.1.5.5.7.3.1"); 822 ekuOids.add("1.3.6.1.5.5.7.3.2"); 823 cbld.addExtendedKeyUsageExt(ekuOids); 824 cbld.addSubjectAltNameDNSExt(Collections.singletonList("localhost")); 825 cbld.addAIAExt(Collections.singletonList(intCaRespURI)); 826 // Make our SSL Server Cert! 827 X509Certificate sslCert = cbld.build(intCaCert, intCaKP.getPrivate(), 828 "SHA256withRSA"); 829 log("SSL Certificate Created:\n" + certInfo(sslCert)); 830 831 // Provide SSL server cert revocation info to the Intermeidate CA 832 // OCSP responder. 833 revInfo = new HashMap<>(); 834 revInfo.put(sslCert.getSerialNumber(), 835 new SimpleOCSPServer.CertStatusInfo( 836 SimpleOCSPServer.CertStatus.CERT_STATUS_GOOD)); 837 intOcsp.updateStatusDb(revInfo); 838 839 // Now build a keystore and add the keys, chain and root cert as a TA 840 serverKeystore = keyStoreBuilder.getKeyStore(); 841 java.security.cert.Certificate[] sslChain = {sslCert, intCaCert, rootCert}; 842 serverKeystore.setKeyEntry(SSL_ALIAS, sslKP.getPrivate(), 843 passwd.toCharArray(), sslChain); 844 serverKeystore.setCertificateEntry(ROOT_ALIAS, rootCert); 845 846 // And finally a Trust Store for the client 847 trustStore = keyStoreBuilder.getKeyStore(); 848 trustStore.setCertificateEntry(ROOT_ALIAS, rootCert); 849 } 850 851 private static void addCommonExts(CertificateBuilder cbld, 852 PublicKey subjKey, PublicKey authKey) throws IOException { 853 cbld.addSubjectKeyIdExt(subjKey); 854 cbld.addAuthorityKeyIdExt(authKey); 855 } 856 857 private static void addCommonCAExts(CertificateBuilder cbld) 858 throws IOException { 859 cbld.addBasicConstraintsExt(true, true, -1); 860 // Set key usage bits for digitalSignature, keyCertSign and cRLSign 861 boolean[] kuBitSettings = {true, false, false, false, false, true, 862 true, false, false}; 863 cbld.addKeyUsageExt(kuBitSettings); 864 } 865 866 /** 867 * Helper routine that dumps only a few cert fields rather than 868 * the whole toString() output. 869 * 870 * @param cert an X509Certificate to be displayed 871 * 872 * @return the String output of the issuer, subject and 873 * serial number 874 */ 875 private static String certInfo(X509Certificate cert) { 876 StringBuilder sb = new StringBuilder(); 877 sb.append("Issuer: ").append(cert.getIssuerX500Principal()). 878 append("\n"); 879 sb.append("Subject: ").append(cert.getSubjectX500Principal()). 880 append("\n"); 881 sb.append("Serial: ").append(cert.getSerialNumber()).append("\n"); 882 return sb.toString(); 883 } 884 885 private static class TestCase { 886 public final String testName; 887 public final ByteBuffer data; 888 public final boolean statReqEnabled; 889 public final boolean statReqV2Enabled; 890 891 TestCase(String name, ByteBuffer buffer, boolean srEn, boolean srv2En) { 892 testName = (name != null) ? name : ""; 893 data = Objects.requireNonNull(buffer, 894 "TestCase requires a non-null ByteBuffer"); 895 statReqEnabled = srEn; 896 statReqV2Enabled = srv2En; 897 } 898 } 899 }