1 /*
2  * Copyright (c) 2004, 2013, 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 //
25 // SunJSSE does not support dynamic system properties, no way to re-use
26 // system properties in samevm/agentvm mode.
27 //
28 
29 /*
30  * @test
31  * @bug 6207322
32  * @summary SSLEngine is returning a premature FINISHED message when doing
33  *     an abbreviated handshake.
34  * @run main/othervm RehandshakeFinished
35  * @author Brad Wetmore
36  */
37 
38 /*
39  * This test may need some updating if the messages change order.
40  * Currently I'm expecting that there is a simple renegotiation, with
41  * each message being contained in a single SSL packet.
42  *
43  *      ClientHello
44  *                              Server Hello
45  *                              CCS
46  *                              FINISHED
47  *      CCS
48  *      FINISHED
49  */
50 
51 /**
52  * A SSLEngine usage example which simplifies the presentation
53  * by removing the I/O and multi-threading concerns.
54  *
55  * The test creates two SSLEngines, simulating a client and server.
56  * The "transport" layer consists two byte buffers:  think of them
57  * as directly connected pipes.
58  *
59  * Note, this is a *very* simple example: real code will be much more
60  * involved.  For example, different threading and I/O models could be
61  * used, transport mechanisms could close unexpectedly, and so on.
62  *
63  * When this application runs, notice that several messages
64  * (wrap/unwrap) pass before any application data is consumed or
65  * produced.  (For more information, please see the SSL/TLS
66  * specifications.)  There may several steps for a successful handshake,
67  * so it's typical to see the following series of operations:
68  *
69  *      client          server          message
70  *      ======          ======          =======
71  *      wrap()          ...             ClientHello
72  *      ...             unwrap()        ClientHello
73  *      ...             wrap()          ServerHello/Certificate
74  *      unwrap()        ...             ServerHello/Certificate
75  *      wrap()          ...             ClientKeyExchange
76  *      wrap()          ...             ChangeCipherSpec
77  *      wrap()          ...             Finished
78  *      ...             unwrap()        ClientKeyExchange
79  *      ...             unwrap()        ChangeCipherSpec
80  *      ...             unwrap()        Finished
81  *      ...             wrap()          ChangeCipherSpec
82  *      ...             wrap()          Finished
83  *      unwrap()        ...             ChangeCipherSpec
84  *      unwrap()        ...             Finished
85  */
86 
87 import javax.net.ssl.*;
88 import javax.net.ssl.SSLEngineResult.*;
89 import java.io.*;
90 import java.security.*;
91 import java.nio.*;
92 
93 public class RehandshakeFinished {
94 
95     /*
96      * Enables logging of the SSLEngine operations.
97      */
98     private static boolean logging = true;
99 
100     /*
101      * Enables the JSSE system debugging system property:
102      *
103      *     -Djavax.net.debug=all
104      *
105      * This gives a lot of low-level information about operations underway,
106      * including specific handshake messages, and might be best examined
107      * after gaining some familiarity with this application.
108      */
109     private static boolean debug = false;
110 
111     static private SSLContext sslc;
112 
113     private SSLEngine clientEngine;     // client Engine
114     private ByteBuffer clientOut;       // write side of clientEngine
115     private ByteBuffer clientIn;        // read side of clientEngine
116 
117     private SSLEngine serverEngine;     // server Engine
118     private ByteBuffer serverOut;       // write side of serverEngine
119     private ByteBuffer serverIn;        // read side of serverEngine
120 
121     /*
122      * For data transport, this example uses local ByteBuffers.  This
123      * isn't really useful, but the purpose of this example is to show
124      * SSLEngine concepts, not how to do network transport.
125      */
126     private ByteBuffer cTOs;            // "reliable" transport client->server
127     private ByteBuffer sTOc;            // "reliable" transport server->client
128 
129     /*
130      * The following is to set up the keystores.
131      */
132     private static String pathToStores = "../../../../javax/net/ssl/etc";
133     private static String keyStoreFile = "keystore";
134     private static String trustStoreFile = "truststore";
135     private static String passwd = "passphrase";
136 
137     private static String keyFilename =
138             System.getProperty("test.src", "./") + "/" + pathToStores +
139                 "/" + keyStoreFile;
140     private static String trustFilename =
141             System.getProperty("test.src", "./") + "/" + pathToStores +
142                 "/" + trustStoreFile;
143 
144     private static Exception loadException = null;
145 
146     static {
147         try {
148             KeyStore ks = KeyStore.getInstance("JKS");
149             KeyStore ts = KeyStore.getInstance("JKS");
150 
151             char[] passphrase = "passphrase".toCharArray();
152 
ks.load(new FileInputStream(keyFilename), passphrase)153             ks.load(new FileInputStream(keyFilename), passphrase);
ts.load(new FileInputStream(trustFilename), passphrase)154             ts.load(new FileInputStream(trustFilename), passphrase);
155 
156             KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
kmf.init(ks, passphrase)157             kmf.init(ks, passphrase);
158 
159             TrustManagerFactory tmf =
160                 TrustManagerFactory.getInstance("SunX509");
161             tmf.init(ts);
162 
163             SSLContext sslCtx = SSLContext.getInstance("TLSv1.2");
kmf.getKeyManagers()164             sslCtx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
165             sslc = sslCtx;
166         } catch (Exception e) {
167             loadException = e;
168         }
169     }
170 
171     /*
172      * Main entry point for this test.
173      */
main(String args[])174     public static void main(String args[]) throws Exception {
175         if (debug) {
176             System.setProperty("javax.net.debug", "all");
177         }
178 
179         if (loadException != null) {
180             throw loadException;
181         }
182 
183         // Prime the session cache with a good session
184         // Second connection should be a simple session resumption.
185         if ((new RehandshakeFinished().runTest()) !=
186                 new RehandshakeFinished().runRehandshake()) {
187             throw new Exception("Sessions not equivalent");
188         }
189 
190         System.out.println("Test Passed.");
191     }
192 
checkResult(SSLEngine engine, SSLEngineResult result, HandshakeStatus rqdHsStatus, boolean consumed, boolean produced)193     private void checkResult(SSLEngine engine, SSLEngineResult result,
194             HandshakeStatus rqdHsStatus,
195             boolean consumed, boolean produced) throws Exception {
196 
197         HandshakeStatus hsStatus = result.getHandshakeStatus();
198 
199         if (hsStatus == HandshakeStatus.NEED_TASK) {
200             Runnable runnable;
201             while ((runnable = engine.getDelegatedTask()) != null) {
202                 runnable.run();
203             }
204             hsStatus = engine.getHandshakeStatus();
205         }
206 
207         if (hsStatus != rqdHsStatus) {
208             throw new Exception("Required " + rqdHsStatus +
209                 ", got " + hsStatus);
210         }
211 
212         int bc = result.bytesConsumed();
213         int bp = result.bytesProduced();
214 
215         if (consumed) {
216             if (bc <= 0) {
217                 throw new Exception("Should have consumed bytes");
218             }
219         } else {
220             if (bc > 0) {
221                 throw new Exception("Should not have consumed bytes");
222             }
223         }
224 
225         if (produced) {
226             if (bp <= 0) {
227                 throw new Exception("Should have produced bytes");
228             }
229         } else {
230             if (bp > 0) {
231                 throw new Exception("Should not have produced bytes");
232             }
233         }
234     }
235 
runRehandshake()236     private SSLSession runRehandshake() throws Exception {
237 
238         log("\n\n==============================================");
239         log("Staring actual test.");
240 
241         createSSLEngines();
242         createBuffers();
243         SSLEngineResult result;
244 
245         log("Client's ClientHello");
246         checkResult(clientEngine,
247             clientEngine.wrap(clientOut, cTOs), HandshakeStatus.NEED_UNWRAP,
248             false, true);
249         cTOs.flip();
250         checkResult(serverEngine,
251             serverEngine.unwrap(cTOs, serverIn), HandshakeStatus.NEED_WRAP,
252             true, false);
253         cTOs.compact();
254 
255         log("Server's ServerHello/ServerHelloDone");
256         checkResult(serverEngine,
257             serverEngine.wrap(serverOut, sTOc), HandshakeStatus.NEED_WRAP,
258             false, true);
259         sTOc.flip();
260         checkResult(clientEngine,
261             clientEngine.unwrap(sTOc, clientIn), HandshakeStatus.NEED_UNWRAP,
262             true, false);
263         sTOc.compact();
264 
265         log("Server's CCS");
266         checkResult(serverEngine,
267             serverEngine.wrap(serverOut, sTOc), HandshakeStatus.NEED_WRAP,
268             false, true);
269         sTOc.flip();
270         checkResult(clientEngine,
271             clientEngine.unwrap(sTOc, clientIn), HandshakeStatus.NEED_UNWRAP,
272             true, false);
273         sTOc.compact();
274 
275         log("Server's FINISHED");
276         checkResult(serverEngine,
277             serverEngine.wrap(serverOut, sTOc), HandshakeStatus.NEED_UNWRAP,
278             false, true);
279         sTOc.flip();
280         checkResult(clientEngine,
281             clientEngine.unwrap(sTOc, clientIn), HandshakeStatus.NEED_WRAP,
282             true, false);
283         sTOc.compact();
284 
285         log("Client's CCS");
286         checkResult(clientEngine,
287             clientEngine.wrap(clientOut, cTOs), HandshakeStatus.NEED_WRAP,
288             false, true);
289         cTOs.flip();
290         checkResult(serverEngine,
291             serverEngine.unwrap(cTOs, serverIn), HandshakeStatus.NEED_UNWRAP,
292             true, false);
293         cTOs.compact();
294 
295         log("Client's FINISHED should trigger FINISHED messages all around.");
296         checkResult(clientEngine,
297             clientEngine.wrap(clientOut, cTOs), HandshakeStatus.FINISHED,
298             false, true);
299         cTOs.flip();
300         checkResult(serverEngine,
301             serverEngine.unwrap(cTOs, serverIn), HandshakeStatus.FINISHED,
302             true, false);
303         cTOs.compact();
304 
305         return clientEngine.getSession();
306     }
307 
308     /*
309      * Run the test.
310      *
311      * Sit in a tight loop, both engines calling wrap/unwrap regardless
312      * of whether data is available or not.  We do this until both engines
313      * report back they are closed.
314      *
315      * The main loop handles all of the I/O phases of the SSLEngine's
316      * lifetime:
317      *
318      *     initial handshaking
319      *     application data transfer
320      *     engine closing
321      *
322      * One could easily separate these phases into separate
323      * sections of code.
324      */
runTest()325     private SSLSession runTest() throws Exception {
326         boolean dataDone = false;
327 
328         createSSLEngines();
329         createBuffers();
330 
331         SSLEngineResult clientResult;   // results from client's last operation
332         SSLEngineResult serverResult;   // results from server's last operation
333 
334         /*
335          * Examining the SSLEngineResults could be much more involved,
336          * and may alter the overall flow of the application.
337          *
338          * For example, if we received a BUFFER_OVERFLOW when trying
339          * to write to the output pipe, we could reallocate a larger
340          * pipe, but instead we wait for the peer to drain it.
341          */
342         while (!isEngineClosed(clientEngine) ||
343                 !isEngineClosed(serverEngine)) {
344 
345             log("================");
346 
347             clientResult = clientEngine.wrap(clientOut, cTOs);
348             log("client wrap: ", clientResult);
349             runDelegatedTasks(clientResult, clientEngine);
350 
351             serverResult = serverEngine.wrap(serverOut, sTOc);
352             log("server wrap: ", serverResult);
353             runDelegatedTasks(serverResult, serverEngine);
354 
355             cTOs.flip();
356             sTOc.flip();
357 
358             log("----");
359 
360             clientResult = clientEngine.unwrap(sTOc, clientIn);
361             log("client unwrap: ", clientResult);
362             runDelegatedTasks(clientResult, clientEngine);
363 
364             serverResult = serverEngine.unwrap(cTOs, serverIn);
365             log("server unwrap: ", serverResult);
366             runDelegatedTasks(serverResult, serverEngine);
367 
368             cTOs.compact();
369             sTOc.compact();
370 
371             /*
372              * After we've transfered all application data between the client
373              * and server, we close the clientEngine's outbound stream.
374              * This generates a close_notify handshake message, which the
375              * server engine receives and responds by closing itself.
376              */
377             if (!dataDone && (clientOut.limit() == serverIn.position()) &&
378                     (serverOut.limit() == clientIn.position())) {
379 
380                 /*
381                  * A sanity check to ensure we got what was sent.
382                  */
383                 checkTransfer(serverOut, clientIn);
384                 checkTransfer(clientOut, serverIn);
385 
386                 log("\tClosing clientEngine's *OUTBOUND*...");
387                 clientEngine.closeOutbound();
388                 dataDone = true;
389             }
390         }
391 
392         return clientEngine.getSession();
393     }
394 
395     /*
396      * Using the SSLContext created during object creation,
397      * create/configure the SSLEngines we'll use for this test.
398      */
createSSLEngines()399     private void createSSLEngines() throws Exception {
400         /*
401          * Configure the serverEngine to act as a server in the SSL/TLS
402          * handshake.  Also, require SSL client authentication.
403          */
404         serverEngine = sslc.createSSLEngine();
405         serverEngine.setUseClientMode(false);
406         serverEngine.setNeedClientAuth(true);
407 
408         /*
409          * Similar to above, but using client mode instead.
410          */
411         clientEngine = sslc.createSSLEngine("client", 80);
412         clientEngine.setUseClientMode(true);
413     }
414 
415     /*
416      * Create and size the buffers appropriately.
417      */
createBuffers()418     private void createBuffers() {
419 
420         /*
421          * We'll assume the buffer sizes are the same
422          * between client and server.
423          */
424         SSLSession session = clientEngine.getSession();
425         int appBufferMax = session.getApplicationBufferSize();
426         int netBufferMax = session.getPacketBufferSize();
427 
428         /*
429          * We'll make the input buffers a bit bigger than the max needed
430          * size, so that unwrap()s following a successful data transfer
431          * won't generate BUFFER_OVERFLOWS.
432          *
433          * We'll use a mix of direct and indirect ByteBuffers for
434          * tutorial purposes only.  In reality, only use direct
435          * ByteBuffers when they give a clear performance enhancement.
436          */
437         clientIn = ByteBuffer.allocate(appBufferMax + 50);
438         serverIn = ByteBuffer.allocate(appBufferMax + 50);
439 
440         cTOs = ByteBuffer.allocateDirect(netBufferMax);
441         sTOc = ByteBuffer.allocateDirect(netBufferMax);
442 
443         clientOut = ByteBuffer.wrap("Hi Server, I'm Client".getBytes());
444         serverOut = ByteBuffer.wrap("Hello Client, I'm Server".getBytes());
445     }
446 
447     /*
448      * If the result indicates that we have outstanding tasks to do,
449      * go ahead and run them in this thread.
450      */
runDelegatedTasks(SSLEngineResult result, SSLEngine engine)451     private static void runDelegatedTasks(SSLEngineResult result,
452             SSLEngine engine) throws Exception {
453 
454         if (result.getHandshakeStatus() == HandshakeStatus.NEED_TASK) {
455             Runnable runnable;
456             while ((runnable = engine.getDelegatedTask()) != null) {
457                 log("\trunning delegated task...");
458                 runnable.run();
459             }
460             HandshakeStatus hsStatus = engine.getHandshakeStatus();
461             if (hsStatus == HandshakeStatus.NEED_TASK) {
462                 throw new Exception(
463                     "handshake shouldn't need additional tasks");
464             }
465             log("\tnew HandshakeStatus: " + hsStatus);
466         }
467     }
468 
isEngineClosed(SSLEngine engine)469     private static boolean isEngineClosed(SSLEngine engine) {
470         return (engine.isOutboundDone() && engine.isInboundDone());
471     }
472 
473     /*
474      * Simple check to make sure everything came across as expected.
475      */
checkTransfer(ByteBuffer a, ByteBuffer b)476     private static void checkTransfer(ByteBuffer a, ByteBuffer b)
477             throws Exception {
478         a.flip();
479         b.flip();
480 
481         if (!a.equals(b)) {
482             throw new Exception("Data didn't transfer cleanly");
483         } else {
484             log("\tData transferred cleanly");
485         }
486 
487         a.position(a.limit());
488         b.position(b.limit());
489         a.limit(a.capacity());
490         b.limit(b.capacity());
491     }
492 
493     /*
494      * Logging code
495      */
496     private static boolean resultOnce = true;
497 
log(String str, SSLEngineResult result)498     private static void log(String str, SSLEngineResult result) {
499         if (!logging) {
500             return;
501         }
502         if (resultOnce) {
503             resultOnce = false;
504             System.out.println("The format of the SSLEngineResult is: \n" +
505                 "\t\"getStatus() / getHandshakeStatus()\" +\n" +
506                 "\t\"bytesConsumed() / bytesProduced()\"\n");
507         }
508         HandshakeStatus hsStatus = result.getHandshakeStatus();
509         log(str +
510             result.getStatus() + "/" + hsStatus + ", " +
511             result.bytesConsumed() + "/" + result.bytesProduced() +
512             " bytes");
513         if (hsStatus == HandshakeStatus.FINISHED) {
514             log("\t...ready for application data");
515         }
516     }
517 
log(String str)518     private static void log(String str) {
519         if (logging) {
520             System.out.println(str);
521         }
522     }
523 }
524