0N/A/*
2362N/A * Copyright (c) 2003, 2007, Oracle and/or its affiliates. All rights reserved.
0N/A * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
0N/A *
0N/A * This code is free software; you can redistribute it and/or modify it
0N/A * under the terms of the GNU General Public License version 2 only, as
0N/A * published by the Free Software Foundation.
0N/A *
0N/A * This code is distributed in the hope that it will be useful, but WITHOUT
0N/A * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
0N/A * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
0N/A * version 2 for more details (a copy is included in the LICENSE file that
0N/A * accompanied this code).
0N/A *
0N/A * You should have received a copy of the GNU General Public License version
0N/A * 2 along with this work; if not, write to the Free Software Foundation,
0N/A * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
0N/A *
2362N/A * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
2362N/A * or visit www.oracle.com if you need additional information or have any
2362N/A * questions.
0N/A */
0N/A
5361N/A//
5361N/A// SunJSSE does not support dynamic system properties, no way to re-use
5361N/A// system properties in samevm/agentvm mode.
5361N/A//
5361N/A
0N/A/*
0N/A * @test
0N/A * @bug 1234567
0N/A * @summary SSLEngine has not yet caused Solaris kernel to panic
5361N/A * @run main/othervm SSLEngineTemplate
0N/A */
0N/A
0N/A/**
0N/A * A SSLEngine usage example which simplifies the presentation
0N/A * by removing the I/O and multi-threading concerns.
0N/A *
0N/A * The test creates two SSLEngines, simulating a client and server.
0N/A * The "transport" layer consists two byte buffers: think of them
0N/A * as directly connected pipes.
0N/A *
0N/A * Note, this is a *very* simple example: real code will be much more
0N/A * involved. For example, different threading and I/O models could be
0N/A * used, transport mechanisms could close unexpectedly, and so on.
0N/A *
0N/A * When this application runs, notice that several messages
0N/A * (wrap/unwrap) pass before any application data is consumed or
0N/A * produced. (For more information, please see the SSL/TLS
0N/A * specifications.) There may several steps for a successful handshake,
0N/A * so it's typical to see the following series of operations:
0N/A *
0N/A * client server message
0N/A * ====== ====== =======
0N/A * wrap() ... ClientHello
0N/A * ... unwrap() ClientHello
0N/A * ... wrap() ServerHello/Certificate
0N/A * unwrap() ... ServerHello/Certificate
0N/A * wrap() ... ClientKeyExchange
0N/A * wrap() ... ChangeCipherSpec
0N/A * wrap() ... Finished
0N/A * ... unwrap() ClientKeyExchange
0N/A * ... unwrap() ChangeCipherSpec
0N/A * ... unwrap() Finished
0N/A * ... wrap() ChangeCipherSpec
0N/A * ... wrap() Finished
0N/A * unwrap() ... ChangeCipherSpec
0N/A * unwrap() ... Finished
0N/A */
0N/A
0N/Aimport javax.net.ssl.*;
0N/Aimport javax.net.ssl.SSLEngineResult.*;
0N/Aimport java.io.*;
0N/Aimport java.security.*;
0N/Aimport java.nio.*;
0N/A
0N/Apublic class SSLEngineTemplate {
0N/A
0N/A /*
0N/A * Enables logging of the SSLEngine operations.
0N/A */
0N/A private static boolean logging = true;
0N/A
0N/A /*
0N/A * Enables the JSSE system debugging system property:
0N/A *
0N/A * -Djavax.net.debug=all
0N/A *
0N/A * This gives a lot of low-level information about operations underway,
0N/A * including specific handshake messages, and might be best examined
0N/A * after gaining some familiarity with this application.
0N/A */
0N/A private static boolean debug = false;
0N/A
0N/A private SSLContext sslc;
0N/A
0N/A private SSLEngine clientEngine; // client Engine
0N/A private ByteBuffer clientOut; // write side of clientEngine
0N/A private ByteBuffer clientIn; // read side of clientEngine
0N/A
0N/A private SSLEngine serverEngine; // server Engine
0N/A private ByteBuffer serverOut; // write side of serverEngine
0N/A private ByteBuffer serverIn; // read side of serverEngine
0N/A
0N/A /*
0N/A * For data transport, this example uses local ByteBuffers. This
0N/A * isn't really useful, but the purpose of this example is to show
0N/A * SSLEngine concepts, not how to do network transport.
0N/A */
0N/A private ByteBuffer cTOs; // "reliable" transport client->server
0N/A private ByteBuffer sTOc; // "reliable" transport server->client
0N/A
0N/A /*
0N/A * The following is to set up the keystores.
0N/A */
0N/A private static String pathToStores = "../etc";
0N/A private static String keyStoreFile = "keystore";
0N/A private static String trustStoreFile = "truststore";
0N/A private static String passwd = "passphrase";
0N/A
0N/A private static String keyFilename =
0N/A System.getProperty("test.src", ".") + "/" + pathToStores +
0N/A "/" + keyStoreFile;
0N/A private static String trustFilename =
0N/A System.getProperty("test.src", ".") + "/" + pathToStores +
0N/A "/" + trustStoreFile;
0N/A
0N/A /*
0N/A * Main entry point for this test.
0N/A */
0N/A public static void main(String args[]) throws Exception {
0N/A if (debug) {
0N/A System.setProperty("javax.net.debug", "all");
0N/A }
0N/A
0N/A SSLEngineTemplate test = new SSLEngineTemplate();
0N/A test.runTest();
0N/A
0N/A System.out.println("Test Passed.");
0N/A }
0N/A
0N/A /*
0N/A * Create an initialized SSLContext to use for these tests.
0N/A */
0N/A public SSLEngineTemplate() throws Exception {
0N/A
0N/A KeyStore ks = KeyStore.getInstance("JKS");
0N/A KeyStore ts = KeyStore.getInstance("JKS");
0N/A
0N/A char[] passphrase = "passphrase".toCharArray();
0N/A
0N/A ks.load(new FileInputStream(keyFilename), passphrase);
0N/A ts.load(new FileInputStream(trustFilename), passphrase);
0N/A
0N/A KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
0N/A kmf.init(ks, passphrase);
0N/A
0N/A TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");
0N/A tmf.init(ts);
0N/A
0N/A SSLContext sslCtx = SSLContext.getInstance("TLS");
0N/A
0N/A sslCtx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
0N/A
0N/A sslc = sslCtx;
0N/A }
0N/A
0N/A /*
0N/A * Run the test.
0N/A *
0N/A * Sit in a tight loop, both engines calling wrap/unwrap regardless
0N/A * of whether data is available or not. We do this until both engines
0N/A * report back they are closed.
0N/A *
0N/A * The main loop handles all of the I/O phases of the SSLEngine's
0N/A * lifetime:
0N/A *
0N/A * initial handshaking
0N/A * application data transfer
0N/A * engine closing
0N/A *
0N/A * One could easily separate these phases into separate
0N/A * sections of code.
0N/A */
0N/A private void runTest() throws Exception {
0N/A boolean dataDone = false;
0N/A
0N/A createSSLEngines();
0N/A createBuffers();
0N/A
0N/A SSLEngineResult clientResult; // results from client's last operation
0N/A SSLEngineResult serverResult; // results from server's last operation
0N/A
0N/A /*
0N/A * Examining the SSLEngineResults could be much more involved,
0N/A * and may alter the overall flow of the application.
0N/A *
0N/A * For example, if we received a BUFFER_OVERFLOW when trying
0N/A * to write to the output pipe, we could reallocate a larger
0N/A * pipe, but instead we wait for the peer to drain it.
0N/A */
0N/A while (!isEngineClosed(clientEngine) ||
0N/A !isEngineClosed(serverEngine)) {
0N/A
0N/A log("================");
0N/A
0N/A clientResult = clientEngine.wrap(clientOut, cTOs);
0N/A log("client wrap: ", clientResult);
0N/A runDelegatedTasks(clientResult, clientEngine);
0N/A
0N/A serverResult = serverEngine.wrap(serverOut, sTOc);
0N/A log("server wrap: ", serverResult);
0N/A runDelegatedTasks(serverResult, serverEngine);
0N/A
0N/A cTOs.flip();
0N/A sTOc.flip();
0N/A
0N/A log("----");
0N/A
0N/A clientResult = clientEngine.unwrap(sTOc, clientIn);
0N/A log("client unwrap: ", clientResult);
0N/A runDelegatedTasks(clientResult, clientEngine);
0N/A
0N/A serverResult = serverEngine.unwrap(cTOs, serverIn);
0N/A log("server unwrap: ", serverResult);
0N/A runDelegatedTasks(serverResult, serverEngine);
0N/A
0N/A cTOs.compact();
0N/A sTOc.compact();
0N/A
0N/A /*
0N/A * After we've transfered all application data between the client
0N/A * and server, we close the clientEngine's outbound stream.
0N/A * This generates a close_notify handshake message, which the
0N/A * server engine receives and responds by closing itself.
0N/A */
0N/A if (!dataDone && (clientOut.limit() == serverIn.position()) &&
0N/A (serverOut.limit() == clientIn.position())) {
0N/A
0N/A /*
0N/A * A sanity check to ensure we got what was sent.
0N/A */
0N/A checkTransfer(serverOut, clientIn);
0N/A checkTransfer(clientOut, serverIn);
0N/A
0N/A log("\tClosing clientEngine's *OUTBOUND*...");
0N/A clientEngine.closeOutbound();
0N/A dataDone = true;
0N/A }
0N/A }
0N/A }
0N/A
0N/A /*
0N/A * Using the SSLContext created during object creation,
0N/A * create/configure the SSLEngines we'll use for this test.
0N/A */
0N/A private void createSSLEngines() throws Exception {
0N/A /*
0N/A * Configure the serverEngine to act as a server in the SSL/TLS
0N/A * handshake. Also, require SSL client authentication.
0N/A */
0N/A serverEngine = sslc.createSSLEngine();
0N/A serverEngine.setUseClientMode(false);
0N/A serverEngine.setNeedClientAuth(true);
0N/A
0N/A /*
0N/A * Similar to above, but using client mode instead.
0N/A */
0N/A clientEngine = sslc.createSSLEngine("client", 80);
0N/A clientEngine.setUseClientMode(true);
0N/A }
0N/A
0N/A /*
0N/A * Create and size the buffers appropriately.
0N/A */
0N/A private void createBuffers() {
0N/A
0N/A /*
0N/A * We'll assume the buffer sizes are the same
0N/A * between client and server.
0N/A */
0N/A SSLSession session = clientEngine.getSession();
0N/A int appBufferMax = session.getApplicationBufferSize();
0N/A int netBufferMax = session.getPacketBufferSize();
0N/A
0N/A /*
0N/A * We'll make the input buffers a bit bigger than the max needed
0N/A * size, so that unwrap()s following a successful data transfer
0N/A * won't generate BUFFER_OVERFLOWS.
0N/A *
0N/A * We'll use a mix of direct and indirect ByteBuffers for
0N/A * tutorial purposes only. In reality, only use direct
0N/A * ByteBuffers when they give a clear performance enhancement.
0N/A */
0N/A clientIn = ByteBuffer.allocate(appBufferMax + 50);
0N/A serverIn = ByteBuffer.allocate(appBufferMax + 50);
0N/A
0N/A cTOs = ByteBuffer.allocateDirect(netBufferMax);
0N/A sTOc = ByteBuffer.allocateDirect(netBufferMax);
0N/A
0N/A clientOut = ByteBuffer.wrap("Hi Server, I'm Client".getBytes());
0N/A serverOut = ByteBuffer.wrap("Hello Client, I'm Server".getBytes());
0N/A }
0N/A
0N/A /*
0N/A * If the result indicates that we have outstanding tasks to do,
0N/A * go ahead and run them in this thread.
0N/A */
0N/A private static void runDelegatedTasks(SSLEngineResult result,
0N/A SSLEngine engine) throws Exception {
0N/A
0N/A if (result.getHandshakeStatus() == HandshakeStatus.NEED_TASK) {
0N/A Runnable runnable;
0N/A while ((runnable = engine.getDelegatedTask()) != null) {
0N/A log("\trunning delegated task...");
0N/A runnable.run();
0N/A }
0N/A HandshakeStatus hsStatus = engine.getHandshakeStatus();
0N/A if (hsStatus == HandshakeStatus.NEED_TASK) {
0N/A throw new Exception(
0N/A "handshake shouldn't need additional tasks");
0N/A }
0N/A log("\tnew HandshakeStatus: " + hsStatus);
0N/A }
0N/A }
0N/A
0N/A private static boolean isEngineClosed(SSLEngine engine) {
0N/A return (engine.isOutboundDone() && engine.isInboundDone());
0N/A }
0N/A
0N/A /*
0N/A * Simple check to make sure everything came across as expected.
0N/A */
0N/A private static void checkTransfer(ByteBuffer a, ByteBuffer b)
0N/A throws Exception {
0N/A a.flip();
0N/A b.flip();
0N/A
0N/A if (!a.equals(b)) {
0N/A throw new Exception("Data didn't transfer cleanly");
0N/A } else {
0N/A log("\tData transferred cleanly");
0N/A }
0N/A
0N/A a.position(a.limit());
0N/A b.position(b.limit());
0N/A a.limit(a.capacity());
0N/A b.limit(b.capacity());
0N/A }
0N/A
0N/A /*
0N/A * Logging code
0N/A */
0N/A private static boolean resultOnce = true;
0N/A
0N/A private static void log(String str, SSLEngineResult result) {
0N/A if (!logging) {
0N/A return;
0N/A }
0N/A if (resultOnce) {
0N/A resultOnce = false;
0N/A System.out.println("The format of the SSLEngineResult is: \n" +
0N/A "\t\"getStatus() / getHandshakeStatus()\" +\n" +
0N/A "\t\"bytesConsumed() / bytesProduced()\"\n");
0N/A }
0N/A HandshakeStatus hsStatus = result.getHandshakeStatus();
0N/A log(str +
0N/A result.getStatus() + "/" + hsStatus + ", " +
0N/A result.bytesConsumed() + "/" + result.bytesProduced() +
0N/A " bytes");
0N/A if (hsStatus == HandshakeStatus.FINISHED) {
0N/A log("\t...ready for application data");
0N/A }
0N/A }
0N/A
0N/A private static void log(String str) {
0N/A if (logging) {
0N/A System.out.println(str);
0N/A }
0N/A }
0N/A}