Path: blob/master/test/jdk/sun/security/ssl/EngineArgs/DebugReportsOneExtraByte.java
41152 views
/*1* Copyright (c) 2003, 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/*24* @test25* @bug 712688926* @summary Incorrect SSLEngine debug output27* @library /test/lib28* @run main DebugReportsOneExtraByte29*/30/*31* Debug output was reporting n+1 bytes of data was written when it was32* really was n.33*34* SunJSSE does not support dynamic system properties, no way to re-use35* system properties in samevm/agentvm mode.36*/3738/**39* A SSLEngine usage example which simplifies the presentation40* by removing the I/O and multi-threading concerns.41*42* The test creates two SSLEngines, simulating a client and server.43* The "transport" layer consists two byte buffers: think of them44* as directly connected pipes.45*46* Note, this is a *very* simple example: real code will be much more47* involved. For example, different threading and I/O models could be48* used, transport mechanisms could close unexpectedly, and so on.49*50* When this application runs, notice that several messages51* (wrap/unwrap) pass before any application data is consumed or52* produced. (For more information, please see the SSL/TLS53* specifications.) There may several steps for a successful handshake,54* so it's typical to see the following series of operations:55*56* client server message57* ====== ====== =======58* wrap() ... ClientHello59* ... unwrap() ClientHello60* ... wrap() ServerHello/Certificate61* unwrap() ... ServerHello/Certificate62* wrap() ... ClientKeyExchange63* wrap() ... ChangeCipherSpec64* wrap() ... Finished65* ... unwrap() ClientKeyExchange66* ... unwrap() ChangeCipherSpec67* ... unwrap() Finished68* ... wrap() ChangeCipherSpec69* ... wrap() Finished70* unwrap() ... ChangeCipherSpec71* unwrap() ... Finished72*/7374import javax.net.ssl.*;75import javax.net.ssl.SSLEngineResult.*;76import java.io.*;77import java.security.*;78import java.nio.*;7980import jdk.test.lib.process.OutputAnalyzer;81import jdk.test.lib.process.ProcessTools;82import jdk.test.lib.security.SecurityUtils;8384public class DebugReportsOneExtraByte {8586/*87* Enables logging of the SSLEngine operations.88*/89private static boolean logging = true;9091private SSLContext sslc;9293private SSLEngine clientEngine; // client Engine94private ByteBuffer clientOut; // write side of clientEngine95private ByteBuffer clientIn; // read side of clientEngine9697private SSLEngine serverEngine; // server Engine98private ByteBuffer serverOut; // write side of serverEngine99private ByteBuffer serverIn; // read side of serverEngine100101/*102* For data transport, this example uses local ByteBuffers. This103* isn't really useful, but the purpose of this example is to show104* SSLEngine concepts, not how to do network transport.105*/106private ByteBuffer cTOs; // "reliable" transport client->server107private ByteBuffer sTOc; // "reliable" transport server->client108109/*110* The following is to set up the keystores.111*/112private static String pathToStores = "../../../../javax/net/ssl/etc";113private static String keyStoreFile = "keystore";114private static String trustStoreFile = "truststore";115private static String passwd = "passphrase";116117private static String keyFilename =118System.getProperty("test.src", ".") + "/" + pathToStores +119"/" + keyStoreFile;120private static String trustFilename =121System.getProperty("test.src", ".") + "/" + pathToStores +122"/" + trustStoreFile;123124/*125* Main entry point for this test.126*/127public static void main(String args[]) throws Exception {128129if (args.length == 0) {130OutputAnalyzer output = ProcessTools.executeTestJvm(131"-Dtest.src=" + System.getProperty("test.src"),132"-Djavax.net.debug=all", "DebugReportsOneExtraByte", "p");133output.shouldContain("WRITE: TLSv1 application_data, length = 8");134135System.out.println("Test Passed.");136} else {137// Re-enable TLSv1 since test depends on it138SecurityUtils.removeFromDisabledTlsAlgs("TLSv1");139140DebugReportsOneExtraByte test = new DebugReportsOneExtraByte();141test.runTest();142}143}144145/*146* Create an initialized SSLContext to use for these tests.147*/148public DebugReportsOneExtraByte() throws Exception {149150KeyStore ks = KeyStore.getInstance("JKS");151KeyStore ts = KeyStore.getInstance("JKS");152153char[] passphrase = "passphrase".toCharArray();154155ks.load(new FileInputStream(keyFilename), passphrase);156ts.load(new FileInputStream(trustFilename), passphrase);157158KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");159kmf.init(ks, passphrase);160161TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");162tmf.init(ts);163164SSLContext sslCtx = SSLContext.getInstance("TLSv1");165166sslCtx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);167168sslc = sslCtx;169}170171/*172* Run the test.173*174* Sit in a tight loop, both engines calling wrap/unwrap regardless175* of whether data is available or not. We do this until both engines176* report back they are closed.177*178* The main loop handles all of the I/O phases of the SSLEngine's179* lifetime:180*181* initial handshaking182* application data transfer183* engine closing184*185* One could easily separate these phases into separate186* sections of code.187*/188private void runTest() throws Exception {189boolean dataDone = false;190191createSSLEngines();192createBuffers();193194SSLEngineResult clientResult; // results from client's last operation195SSLEngineResult serverResult; // results from server's last operation196197/*198* Examining the SSLEngineResults could be much more involved,199* and may alter the overall flow of the application.200*201* For example, if we received a BUFFER_OVERFLOW when trying202* to write to the output pipe, we could reallocate a larger203* pipe, but instead we wait for the peer to drain it.204*/205206/*207* Write one byte in first application packet, the rest208* will come later.209*/210serverOut.limit(1);211212while (!isEngineClosed(clientEngine) ||213!isEngineClosed(serverEngine)) {214215log("================");216217clientResult = clientEngine.wrap(clientOut, cTOs);218log("client wrap: ", clientResult);219runDelegatedTasks(clientResult, clientEngine);220221serverResult = serverEngine.wrap(serverOut, sTOc);222log("server wrap: ", serverResult);223runDelegatedTasks(serverResult, serverEngine);224225// Next wrap will split.226if (serverOut.position() == 1) {227serverOut.limit(serverOut.capacity());228}229230cTOs.flip();231sTOc.flip();232233log("----");234235clientResult = clientEngine.unwrap(sTOc, clientIn);236log("client unwrap: ", clientResult);237runDelegatedTasks(clientResult, clientEngine);238239serverResult = serverEngine.unwrap(cTOs, serverIn);240log("server unwrap: ", serverResult);241runDelegatedTasks(serverResult, serverEngine);242243cTOs.compact();244sTOc.compact();245246/*247* After we've transfered all application data between the client248* and server, we close the clientEngine's outbound stream.249* This generates a close_notify handshake message, which the250* server engine receives and responds by closing itself.251*/252if (!dataDone && (clientOut.limit() == serverIn.position()) &&253(serverOut.limit() == clientIn.position())) {254255/*256* A sanity check to ensure we got what was sent.257*/258checkTransfer(serverOut, clientIn);259checkTransfer(clientOut, serverIn);260261log("\tClosing clientEngine's *OUTBOUND*...");262clientEngine.closeOutbound();263dataDone = true;264}265}266}267268/*269* Using the SSLContext created during object creation,270* create/configure the SSLEngines we'll use for this test.271*/272private void createSSLEngines() throws Exception {273/*274* Configure the serverEngine to act as a server in the SSL/TLS275* handshake. Also, require SSL client authentication.276*/277serverEngine = sslc.createSSLEngine();278serverEngine.setUseClientMode(false);279serverEngine.setNeedClientAuth(true);280281// Force a block-oriented ciphersuite.282serverEngine.setEnabledCipherSuites(283new String [] {"TLS_RSA_WITH_AES_128_CBC_SHA"});284285/*286* Similar to above, but using client mode instead.287*/288clientEngine = sslc.createSSLEngine("client", 80);289clientEngine.setUseClientMode(true);290}291292/*293* Create and size the buffers appropriately.294*/295private void createBuffers() {296297/*298* We'll assume the buffer sizes are the same299* between client and server.300*/301SSLSession session = clientEngine.getSession();302int appBufferMax = session.getApplicationBufferSize();303int netBufferMax = session.getPacketBufferSize();304305/*306* We'll make the input buffers a bit bigger than the max needed307* size, so that unwrap()s following a successful data transfer308* won't generate BUFFER_OVERFLOWS.309*310* We'll use a mix of direct and indirect ByteBuffers for311* tutorial purposes only. In reality, only use direct312* ByteBuffers when they give a clear performance enhancement.313*/314clientIn = ByteBuffer.allocate(appBufferMax + 50);315serverIn = ByteBuffer.allocate(appBufferMax + 50);316317cTOs = ByteBuffer.allocateDirect(netBufferMax);318sTOc = ByteBuffer.allocateDirect(netBufferMax);319320// No need to write anything on the client side, it will321// just confuse the output.322clientOut = ByteBuffer.wrap("".getBytes());323// 10 bytes long324serverOut = ByteBuffer.wrap("Hi Client!".getBytes());325}326327/*328* If the result indicates that we have outstanding tasks to do,329* go ahead and run them in this thread.330*/331private static void runDelegatedTasks(SSLEngineResult result,332SSLEngine engine) throws Exception {333334if (result.getHandshakeStatus() == HandshakeStatus.NEED_TASK) {335Runnable runnable;336while ((runnable = engine.getDelegatedTask()) != null) {337log("\trunning delegated task...");338runnable.run();339}340HandshakeStatus hsStatus = engine.getHandshakeStatus();341if (hsStatus == HandshakeStatus.NEED_TASK) {342throw new Exception(343"handshake shouldn't need additional tasks");344}345log("\tnew HandshakeStatus: " + hsStatus);346}347}348349private static boolean isEngineClosed(SSLEngine engine) {350return (engine.isOutboundDone() && engine.isInboundDone());351}352353/*354* Simple check to make sure everything came across as expected.355*/356private static void checkTransfer(ByteBuffer a, ByteBuffer b)357throws Exception {358a.flip();359b.flip();360361if (!a.equals(b)) {362throw new Exception("Data didn't transfer cleanly");363} else {364log("\tData transferred cleanly");365}366367a.position(a.limit());368b.position(b.limit());369a.limit(a.capacity());370b.limit(b.capacity());371}372373/*374* Logging code375*/376private static boolean resultOnce = true;377378private static void log(String str, SSLEngineResult result) {379if (!logging) {380return;381}382if (resultOnce) {383resultOnce = false;384System.out.println("The format of the SSLEngineResult is: \n" +385"\t\"getStatus() / getHandshakeStatus()\" +\n" +386"\t\"bytesConsumed() / bytesProduced()\"\n");387}388HandshakeStatus hsStatus = result.getHandshakeStatus();389log(str +390result.getStatus() + "/" + hsStatus + ", " +391result.bytesConsumed() + "/" + result.bytesProduced() +392" bytes");393if (hsStatus == HandshakeStatus.FINISHED) {394log("\t...ready for application data");395}396}397398private static void log(String str) {399if (logging) {400System.out.println(str);401}402}403}404405406