Path: blob/master/test/jdk/javax/net/ssl/SSLEngine/FinishedPresent.java
41152 views
/*1* Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation.7*8* This code is distributed in the hope that it will be useful, but WITHOUT9* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or10* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License11* version 2 for more details (a copy is included in the LICENSE file that12* accompanied this code).13*14* You should have received a copy of the GNU General Public License version15* 2 along with this work; if not, write to the Free Software Foundation,16* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.17*18* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA19* or visit www.oracle.com if you need additional information or have any20* questions.21*/2223// SunJSSE does not support dynamic system properties, no way to re-use24// system properties in samevm/agentvm mode.2526/*27* @test28* @bug 823361929* @summary SSLEngine has not yet caused Solaris kernel to panic30* @run main/othervm FinishedPresent31*/32import javax.net.ssl.*;33import javax.net.ssl.SSLEngineResult.*;34import java.io.*;35import java.security.*;36import java.nio.*;3738public class FinishedPresent {3940/*41* Enables logging of the SSLEngine operations.42*/43private static final boolean logging = true;4445/*46* Enables the JSSE system debugging system property:47*48* -Djavax.net.debug=all49*50* This gives a lot of low-level information about operations underway,51* including specific handshake messages, and might be best examined52* after gaining some familiarity with this application.53*/54private static final boolean debug = false;5556private final SSLContext sslc;5758private SSLEngine clientEngine; // client Engine59private ByteBuffer clientOut; // write side of clientEngine60private ByteBuffer clientIn; // read side of clientEngine6162private SSLEngine serverEngine; // server Engine63private ByteBuffer serverOut; // write side of serverEngine64private ByteBuffer serverIn; // read side of serverEngine6566/*67* For data transport, this example uses local ByteBuffers. This68* isn't really useful, but the purpose of this example is to show69* SSLEngine concepts, not how to do network transport.70*/71private ByteBuffer cTOs; // "reliable" transport client->server72private ByteBuffer sTOc; // "reliable" transport server->client7374/*75* The following is to set up the keystores.76*/77private static final String pathToStores = "../etc";78private static final String keyStoreFile = "keystore";79private static final String trustStoreFile = "truststore";80private static final char[] passphrase = "passphrase".toCharArray();8182private static final String keyFilename =83System.getProperty("test.src", ".") + "/" + pathToStores +84"/" + keyStoreFile;85private static final String trustFilename =86System.getProperty("test.src", ".") + "/" + pathToStores +87"/" + trustStoreFile;8889/*90* Main entry point for this test.91*/92public static void main(String args[]) throws Exception {93if (debug) {94System.setProperty("javax.net.debug", "all");95}9697FinishedPresent test = new FinishedPresent();98test.runTest();99100log("Test Passed.");101}102103/*104* Create an initialized SSLContext to use for these tests.105*/106public FinishedPresent() throws Exception {107108KeyStore ks = KeyStore.getInstance("JKS");109KeyStore ts = KeyStore.getInstance("JKS");110111ks.load(new FileInputStream(keyFilename), passphrase);112ts.load(new FileInputStream(trustFilename), passphrase);113114KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");115kmf.init(ks, passphrase);116117TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");118tmf.init(ts);119120SSLContext sslCtx = SSLContext.getInstance("TLSv1.3");121122sslCtx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);123124sslc = sslCtx;125}126127/*128* Run the test.129*130* Sit in a tight loop, both engines calling wrap/unwrap regardless131* of whether data is available or not. We do this until both engines132* report back they are closed.133*134* The main loop handles all of the I/O phases of the SSLEngine's135* lifetime:136*137* initial handshaking138* application data transfer139* engine closing140*141* One could easily separate these phases into separate142* sections of code.143*/144private void runTest() throws Exception {145boolean dataDone = false;146147createSSLEngines();148createBuffers();149150// results from client's last operation151SSLEngineResult clientResult;152153// results from server's last operation154SSLEngineResult serverResult;155156/*157* Examining the SSLEngineResults could be much more involved,158* and may alter the overall flow of the application.159*160* For example, if we received a BUFFER_OVERFLOW when trying161* to write to the output pipe, we could reallocate a larger162* pipe, but instead we wait for the peer to drain it.163*/164Exception clientException = null;165Exception serverException = null;166167boolean clientFinishedPresent = false;168boolean serverFinishedPresent = false;169boolean client2ndFinishedPresent = false;170boolean server2ndFinishedPresent = false;171while (!isEngineClosed(clientEngine)172|| !isEngineClosed(serverEngine)) {173174log("================");175176try {177clientResult = clientEngine.wrap(clientOut, cTOs);178if (clientFinishedPresent) {179client2ndFinishedPresent |= hasFinished(180"posthandshake client wrap", clientResult);181} else {182clientFinishedPresent |= hasFinished(183"client wrap", clientResult);184}185log("client wrap: ", clientResult);186} catch (Exception e) {187clientException = e;188log("Client wrap() threw: " + e.getMessage());189}190logEngineStatus(clientEngine);191runDelegatedTasks(clientEngine);192193log("----");194195try {196serverResult = serverEngine.wrap(serverOut, sTOc);197if (serverFinishedPresent) {198server2ndFinishedPresent |= hasFinished(199"posthandshake server wrap", serverResult);200} else {201serverFinishedPresent |= hasFinished(202"server wrap", serverResult);203}204log("server wrap: ", serverResult);205} catch (Exception e) {206serverException = e;207log("Server wrap() threw: " + e.getMessage());208}209logEngineStatus(serverEngine);210runDelegatedTasks(serverEngine);211212cTOs.flip();213sTOc.flip();214215log("--------");216217try {218clientResult = clientEngine.unwrap(sTOc, clientIn);219if (clientFinishedPresent) {220client2ndFinishedPresent |= hasFinished(221"posthandshake client unwrap", clientResult);222} else {223clientFinishedPresent |= hasFinished(224"client unwrap", clientResult);225}226log("client unwrap: ", clientResult);227} catch (Exception e) {228clientException = e;229log("Client unwrap() threw: " + e.getMessage());230}231logEngineStatus(clientEngine);232runDelegatedTasks(clientEngine);233234log("----");235236try {237serverResult = serverEngine.unwrap(cTOs, serverIn);238if (serverFinishedPresent) {239server2ndFinishedPresent |= hasFinished(240"posthandshake server unwrap", serverResult);241} else {242serverFinishedPresent |= hasFinished(243"server unwrap", serverResult);244}245log("server unwrap: ", serverResult);246} catch (Exception e) {247serverException = e;248log("Server unwrap() threw: " + e.getMessage());249}250logEngineStatus(serverEngine);251runDelegatedTasks(serverEngine);252253cTOs.compact();254sTOc.compact();255256/*257* After we've transfered all application data between the client258* and server, we close the clientEngine's outbound stream.259* This generates a close_notify handshake message, which the260* server engine receives and responds by closing itself.261*/262if (!dataDone && (clientOut.limit() == serverIn.position()) &&263(serverOut.limit() == clientIn.position())) {264265/*266* A sanity check to ensure we got what was sent.267*/268checkTransfer(serverOut, clientIn);269checkTransfer(clientOut, serverIn);270271log("\tClosing clientEngine's *OUTBOUND*...");272clientEngine.closeOutbound();273logEngineStatus(clientEngine);274275dataDone = true;276log("\tClosing serverEngine's *OUTBOUND*...");277serverEngine.closeOutbound();278logEngineStatus(serverEngine);279}280}281282if (!clientFinishedPresent) {283throw new Exception("No client FINISHED status present");284}285286if (!serverFinishedPresent) {287throw new Exception("No server FINISHED status present");288}289290if (!client2ndFinishedPresent) {291throw new Exception(292"No posthandshake client FINISHED status present");293}294295// Note: the server side did not finish the handshake unless the296// posthandshake message get delivered. This behaviro may be297// updated in the future.298//299// if (!server2ndFinishedPresent) {300// throw new Exception(301// "No posthandshake server FINISHED status present");302// }303}304305private static void logEngineStatus(SSLEngine engine) {306log("\tCurrent HS State " + engine.getHandshakeStatus().toString());307log("\tisInboundDone(): " + engine.isInboundDone());308log("\tisOutboundDone(): " + engine.isOutboundDone());309}310311private static boolean hasFinished(312String prefix, SSLEngineResult engineResult) {313if (engineResult.getHandshakeStatus() == HandshakeStatus.FINISHED) {314log(prefix + " finished present: " + engineResult);315return true;316}317318return false;319}320321/*322* Using the SSLContext created during object creation,323* create/configure the SSLEngines we'll use for this test.324*/325private void createSSLEngines() throws Exception {326/*327* Configure the serverEngine to act as a server in the SSL/TLS328* handshake. Also, require SSL client authentication.329*/330serverEngine = sslc.createSSLEngine();331serverEngine.setUseClientMode(false);332serverEngine.setNeedClientAuth(true);333334// Get/set parameters if needed335SSLParameters paramsServer = serverEngine.getSSLParameters();336serverEngine.setSSLParameters(paramsServer);337338/*339* Similar to above, but using client mode instead.340*/341clientEngine = sslc.createSSLEngine("client", 80);342clientEngine.setUseClientMode(true);343344// Get/set parameters if needed345SSLParameters paramsClient = clientEngine.getSSLParameters();346clientEngine.setSSLParameters(paramsClient);347}348349/*350* Create and size the buffers appropriately.351*/352private void createBuffers() {353354/*355* We'll assume the buffer sizes are the same356* between client and server.357*/358SSLSession session = clientEngine.getSession();359int appBufferMax = session.getApplicationBufferSize();360int netBufferMax = session.getPacketBufferSize();361362/*363* We'll make the input buffers a bit bigger than the max needed364* size, so that unwrap()s following a successful data transfer365* won't generate BUFFER_OVERFLOWS.366*367* We'll use a mix of direct and indirect ByteBuffers for368* tutorial purposes only. In reality, only use direct369* ByteBuffers when they give a clear performance enhancement.370*/371clientIn = ByteBuffer.allocate(appBufferMax + 50);372serverIn = ByteBuffer.allocate(appBufferMax + 50);373374cTOs = ByteBuffer.allocateDirect(netBufferMax);375sTOc = ByteBuffer.allocateDirect(netBufferMax);376377clientOut = ByteBuffer.wrap("Hi Server, I'm Client".getBytes());378serverOut = ByteBuffer.wrap("Hello Client, I'm Server".getBytes());379}380381/*382* If the result indicates that we have outstanding tasks to do,383* go ahead and run them in this thread.384*/385private static void runDelegatedTasks(SSLEngine engine) throws Exception {386387if (engine.getHandshakeStatus() == HandshakeStatus.NEED_TASK) {388Runnable runnable;389while ((runnable = engine.getDelegatedTask()) != null) {390log(" running delegated task...");391runnable.run();392}393HandshakeStatus hsStatus = engine.getHandshakeStatus();394if (hsStatus == HandshakeStatus.NEED_TASK) {395throw new Exception(396"handshake shouldn't need additional tasks");397}398logEngineStatus(engine);399}400}401402private static boolean isEngineClosed(SSLEngine engine) {403return (engine.isOutboundDone() && engine.isInboundDone());404}405406/*407* Simple check to make sure everything came across as expected.408*/409private static void checkTransfer(ByteBuffer a, ByteBuffer b)410throws Exception {411a.flip();412b.flip();413414if (!a.equals(b)) {415throw new Exception("Data didn't transfer cleanly");416} else {417log("\tData transferred cleanly");418}419420a.position(a.limit());421b.position(b.limit());422a.limit(a.capacity());423b.limit(b.capacity());424}425426/*427* Logging code428*/429private static boolean resultOnce = true;430431private static void log(String str, SSLEngineResult result) {432if (!logging) {433return;434}435if (resultOnce) {436resultOnce = false;437System.err.println("The format of the SSLEngineResult is: \n" +438"\t\"getStatus() / getHandshakeStatus()\" +\n" +439"\t\"bytesConsumed() / bytesProduced()\"\n");440}441HandshakeStatus hsStatus = result.getHandshakeStatus();442log(str +443result.getStatus() + "/" + hsStatus + ", " +444result.bytesConsumed() + "/" + result.bytesProduced() +445" bytes");446if (hsStatus == HandshakeStatus.FINISHED) {447log("\t...ready for application data");448}449}450451private static void log(String str) {452if (logging) {453System.err.println(str);454}455}456}457458459