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
29package sun.security.ssl;
30
31import javax.net.ssl.*;
32import javax.net.ssl.SSLEngineResult.*;
33import javax.security.auth.x500.X500Principal;
34import java.io.*;
35import java.math.BigInteger;
36import java.security.*;
37import java.nio.*;
38import java.security.cert.X509Certificate;
39import java.security.cert.Extension;
40import java.util.ArrayList;
41import java.util.Collections;
42import java.util.Date;
43import java.util.HashMap;
44import java.util.List;
45import java.util.Map;
46import java.util.Objects;
47import java.util.concurrent.TimeUnit;
48
49import sun.security.provider.certpath.OCSPNonceExtension;
50import sun.security.provider.certpath.ResponderId;
51import sun.security.testlibrary.SimpleOCSPServer;
52import sun.security.testlibrary.CertificateBuilder;
53
54public 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}
900