1 /*
2  * Copyright (c) 2003, 2019, 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 4495742 8190492
32  * @summary Demonstrate SSLEngine switch from no client auth to client auth.
33  * @run main/othervm NoAuthClientAuth SSLv3
34  * @run main/othervm NoAuthClientAuth TLSv1
35  * @run main/othervm NoAuthClientAuth TLSv1.1
36  * @run main/othervm NoAuthClientAuth TLSv1.2
37  * @author Brad R. Wetmore
38  */
39 
40 /**
41  * A SSLEngine usage example which simplifies the presentation
42  * by removing the I/O and multi-threading concerns.
43  *
44  * The test creates two SSLEngines, simulating a client and server.
45  * The "transport" layer consists two byte buffers:  think of them
46  * as directly connected pipes.
47  *
48  * Note, this is a *very* simple example: real code will be much more
49  * involved.  For example, different threading and I/O models could be
50  * used, transport mechanisms could close unexpectedly, and so on.
51  *
52  * When this application runs, notice that several messages
53  * (wrap/unwrap) pass before any application data is consumed or
54  * produced.  (For more information, please see the SSL/TLS
55  * specifications.)  There may several steps for a successful handshake,
56  * so it's typical to see the following series of operations:
57  *
58  *      client          server          message
59  *      ======          ======          =======
60  *      wrap()          ...             ClientHello
61  *      ...             unwrap()        ClientHello
62  *      ...             wrap()          ServerHello/Certificate
63  *      unwrap()        ...             ServerHello/Certificate
64  *      wrap()          ...             ClientKeyExchange
65  *      wrap()          ...             ChangeCipherSpec
66  *      wrap()          ...             Finished
67  *      ...             unwrap()        ClientKeyExchange
68  *      ...             unwrap()        ChangeCipherSpec
69  *      ...             unwrap()        Finished
70  *      ...             wrap()          ChangeCipherSpec
71  *      ...             wrap()          Finished
72  *      unwrap()        ...             ChangeCipherSpec
73  *      unwrap()        ...             Finished
74  *
75  * In this example, we do a rehandshake and make sure that completes
76  * correctly.
77  */
78 
79 import javax.net.ssl.*;
80 import javax.net.ssl.SSLEngineResult.*;
81 import java.io.*;
82 import java.security.*;
83 import java.nio.*;
84 
85 // Note that this test case depends on JSSE provider implementation details.
86 public class NoAuthClientAuth {
87 
88     /*
89      * Enables logging of the SSLEngine operations.
90      */
91     private static boolean logging = true;
92 
93     /*
94      * Enables the JSSE system debugging system property:
95      *
96      *     -Djavax.net.debug=all
97      *
98      * This gives a lot of low-level information about operations underway,
99      * including specific handshake messages, and might be best examined
100      * after gaining some familiarity with this application.
101      */
102     private static boolean debug = true;
103 
104     private SSLContext sslc;
105 
106     private SSLEngine clientEngine;     // client Engine
107     private ByteBuffer clientOut;       // write side of clientEngine
108     private ByteBuffer clientIn;        // read side of clientEngine
109 
110     private SSLEngine serverEngine;     // server Engine
111     private ByteBuffer serverOut;       // write side of serverEngine
112     private ByteBuffer serverIn;        // read side of serverEngine
113 
114     /*
115      * For data transport, this example uses local ByteBuffers.  This
116      * isn't really useful, but the purpose of this example is to show
117      * SSLEngine concepts, not how to do network transport.
118      */
119     private ByteBuffer cTOs;            // "reliable" transport client->server
120     private ByteBuffer sTOc;            // "reliable" transport server->client
121 
122     /*
123      * The following is to set up the keystores.
124      */
125     private static String pathToStores = "../etc";
126     private static String keyStoreFile = "keystore";
127     private static String trustStoreFile = "truststore";
128     private static String passwd = "passphrase";
129 
130     private static String keyFilename =
131             System.getProperty("test.src", ".") + "/" + pathToStores +
132                 "/" + keyStoreFile;
133     private static String trustFilename =
134             System.getProperty("test.src", ".") + "/" + pathToStores +
135                 "/" + trustStoreFile;
136     // the specified protocol
137     private static String tlsProtocol;
138 
139     /*
140      * Main entry point for this test.
141      */
main(String args[])142     public static void main(String args[]) throws Exception {
143         Security.setProperty("jdk.tls.disabledAlgorithms", "");
144 
145         if (debug) {
146             System.setProperty("javax.net.debug", "all");
147         }
148 
149         tlsProtocol = args[0];
150 
151         NoAuthClientAuth test = new NoAuthClientAuth();
152         test.runTest();
153 
154         System.out.println("Test Passed.");
155     }
156 
157     /*
158      * Create an initialized SSLContext to use for these tests.
159      */
NoAuthClientAuth()160     public NoAuthClientAuth() throws Exception {
161 
162         KeyStore ks = KeyStore.getInstance("JKS");
163         KeyStore ts = KeyStore.getInstance("JKS");
164 
165         char[] passphrase = "passphrase".toCharArray();
166 
167         ks.load(new FileInputStream(keyFilename), passphrase);
168         ts.load(new FileInputStream(trustFilename), passphrase);
169 
170         KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
171         kmf.init(ks, passphrase);
172 
173         TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");
174         tmf.init(ts);
175 
176         SSLContext sslCtx = SSLContext.getInstance("TLS");
177 
178         sslCtx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
179 
180         sslc = sslCtx;
181     }
182 
183     /*
184      * Run the test.
185      *
186      * Sit in a tight loop, both engines calling wrap/unwrap regardless
187      * of whether data is available or not.  We do this until both engines
188      * report back they are closed.
189      *
190      * The main loop handles all of the I/O phases of the SSLEngine's
191      * lifetime:
192      *
193      *     initial handshaking
194      *     application data transfer
195      *     engine closing
196      *
197      * One could easily separate these phases into separate
198      * sections of code.
199      */
runTest()200     private void runTest() throws Exception {
201 
202         createSSLEngines();
203         createBuffers();
204 
205         SSLEngineResult clientResult;   // results from client's last operation
206         SSLEngineResult serverResult;   // results from server's last operation
207 
208         /*
209          * Examining the SSLEngineResults could be much more involved,
210          * and may alter the overall flow of the application.
211          *
212          * For example, if we received a BUFFER_OVERFLOW when trying
213          * to write to the output pipe, we could reallocate a larger
214          * pipe, but instead we wait for the peer to drain it.
215          */
216         int hsCompleted = 0;
217         while (!isEngineClosed(clientEngine) ||
218                 !isEngineClosed(serverEngine)) {
219 
220             log("================");
221 
222             clientResult = clientEngine.wrap(clientOut, cTOs);
223             log("client wrap: ", clientResult);
224             runDelegatedTasks(clientResult, clientEngine);
225             clientOut.rewind();
226 
227             serverResult = serverEngine.wrap(serverOut, sTOc);
228             log("server wrap: ", serverResult);
229             runDelegatedTasks(serverResult, serverEngine);
230             serverOut.rewind();
231 
232             // Jeanfrancois:
233             // Here is the main rehandshaking step.
234             if (serverResult.getHandshakeStatus() ==
235                     HandshakeStatus.FINISHED) {
236                 hsCompleted++;
237                 log("\t" + hsCompleted + " handshake completed");
238                 if (hsCompleted == 1) {
239                     try {
240                         serverEngine.getSession().getPeerCertificates();
241                         throw new Exception("Should have got exception");
242                     } catch (SSLPeerUnverifiedException e) {
243                         System.out.println("Caught proper exception." + e);
244                     }
245                     log("\tInvalidating session, setting client auth, " +
246                         " starting rehandshake");
247                     serverEngine.getSession().invalidate();
248                     serverEngine.setNeedClientAuth(true);
249                     serverEngine.beginHandshake();
250                 } else if (hsCompleted == 2) {
251                     java.security.cert.Certificate [] certs =
252                         serverEngine.getSession().getPeerCertificates();
253                     System.out.println("Client Certificate(s) received");
254                     for (java.security.cert.Certificate c : certs) {
255                         System.out.println(c);
256                     }
257 //                    log("Closing server.");
258 //                    serverEngine.closeOutbound();
259                 } // nothing.
260             }
261 
262             cTOs.flip();
263             sTOc.flip();
264 
265             log("----");
266 
267             if (!clientEngine.isInboundDone()) {
268                 clientResult = clientEngine.unwrap(sTOc, clientIn);
269                 log("client unwrap: ", clientResult);
270                 runDelegatedTasks(clientResult, clientEngine);
271                 clientIn.clear();
272                 sTOc.compact();
273             } else {
274                 sTOc.clear();
275             }
276 
277             if (!serverEngine.isInboundDone()) {
278                 serverResult = serverEngine.unwrap(cTOs, serverIn);
279                 log("server unwrap: ", serverResult);
280                 runDelegatedTasks(serverResult, serverEngine);
281                 serverIn.clear();
282                 cTOs.compact();
283             } else {
284                 cTOs.clear();
285             }
286 
287             if (hsCompleted == 2) {
288                   log("Closing server.");
289                   serverEngine.closeOutbound();
290             }
291         }
292     }
293 
294     /*
295      * Using the SSLContext created during object creation,
296      * create/configure the SSLEngines we'll use for this test.
297      */
createSSLEngines()298     private void createSSLEngines() throws Exception {
299         /*
300          * Configure the serverEngine to act as a server in the SSL/TLS
301          * handshake.  Also, require SSL client authentication.
302          */
303         serverEngine = sslc.createSSLEngine();
304         serverEngine.setUseClientMode(false);
305         serverEngine.setNeedClientAuth(false);
306 
307         // Enable all supported protocols on server side to test SSLv3
308         if ("SSLv3".equals(tlsProtocol)) {
309             serverEngine.setEnabledProtocols(serverEngine.getSupportedProtocols());
310         }
311 
312         /*
313          * Similar to above, but using client mode instead.
314          */
315         clientEngine = sslc.createSSLEngine("client", 80);
316         clientEngine.setUseClientMode(true);
317         clientEngine.setEnabledProtocols(new String[] { tlsProtocol });
318     }
319 
320     /*
321      * Create and size the buffers appropriately.
322      */
createBuffers()323     private void createBuffers() {
324 
325         /*
326          * We'll assume the buffer sizes are the same
327          * between client and server.
328          */
329         SSLSession session = clientEngine.getSession();
330         int appBufferMax = session.getApplicationBufferSize();
331         int netBufferMax = session.getPacketBufferSize();
332 
333         /*
334          * We'll make the input buffers a bit bigger than the max needed
335          * size, so that unwrap()s following a successful data transfer
336          * won't generate BUFFER_OVERFLOWS.
337          *
338          * We'll use a mix of direct and indirect ByteBuffers for
339          * tutorial purposes only.  In reality, only use direct
340          * ByteBuffers when they give a clear performance enhancement.
341          */
342         clientIn = ByteBuffer.allocate(appBufferMax + 50);
343         serverIn = ByteBuffer.allocate(appBufferMax + 50);
344 
345         cTOs = ByteBuffer.allocateDirect(netBufferMax);
346         sTOc = ByteBuffer.allocateDirect(netBufferMax);
347 
348         clientOut = ByteBuffer.wrap("Hi Server, I'm Client".getBytes());
349         serverOut = ByteBuffer.wrap("Hello Client, I'm Server".getBytes());
350     }
351 
352     /*
353      * If the result indicates that we have outstanding tasks to do,
354      * go ahead and run them in this thread.
355      */
runDelegatedTasks(SSLEngineResult result, SSLEngine engine)356     private static void runDelegatedTasks(SSLEngineResult result,
357             SSLEngine engine) throws Exception {
358 
359         if (result.getHandshakeStatus() == HandshakeStatus.NEED_TASK) {
360             Runnable runnable;
361             while ((runnable = engine.getDelegatedTask()) != null) {
362                 log("\trunning delegated task...");
363                 runnable.run();
364             }
365             HandshakeStatus hsStatus = engine.getHandshakeStatus();
366             if (hsStatus == HandshakeStatus.NEED_TASK) {
367                 throw new Exception(
368                     "handshake shouldn't need additional tasks");
369             }
370             log("\tnew HandshakeStatus: " + hsStatus);
371         }
372     }
373 
isEngineClosed(SSLEngine engine)374     private static boolean isEngineClosed(SSLEngine engine) {
375         return (engine.isOutboundDone() && engine.isInboundDone());
376     }
377 
378     /*
379      * Simple check to make sure everything came across as expected.
380      */
checkTransfer(ByteBuffer a, ByteBuffer b)381     private static void checkTransfer(ByteBuffer a, ByteBuffer b)
382             throws Exception {
383         a.flip();
384         b.flip();
385 
386         if (!a.equals(b)) {
387             throw new Exception("Data didn't transfer cleanly");
388         } else {
389             log("\tData transferred cleanly");
390         }
391 
392         a.position(a.limit());
393         b.position(b.limit());
394         a.limit(a.capacity());
395         b.limit(b.capacity());
396     }
397 
398     /*
399      * Logging code
400      */
401     private static boolean resultOnce = true;
402 
log(String str, SSLEngineResult result)403     private static void log(String str, SSLEngineResult result) {
404         if (!logging) {
405             return;
406         }
407         if (resultOnce) {
408             resultOnce = false;
409             System.out.println("The format of the SSLEngineResult is: \n" +
410                 "\t\"getStatus() / getHandshakeStatus()\" +\n" +
411                 "\t\"bytesConsumed() / bytesProduced()\"\n");
412         }
413         HandshakeStatus hsStatus = result.getHandshakeStatus();
414         log(str +
415             result.getStatus() + "/" + hsStatus + ", " +
416             result.bytesConsumed() + "/" + result.bytesProduced() +
417             " bytes");
418         if (hsStatus == HandshakeStatus.FINISHED) {
419             log("\t...ready for application data");
420         }
421     }
422 
log(String str)423     private static void log(String str) {
424         if (logging) {
425             System.out.println(str);
426         }
427     }
428 }
429