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