Path: blob/master/test/jdk/javax/net/ssl/templates/SSLSocketSSLEngineTemplate.java
41152 views
/*1* Copyright (c) 2011, 2019, 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// SunJSSE does not support dynamic system properties, no way to re-use25// system properties in samevm/agentvm mode.26//2728/*29* @test30* @bug 710578031* @summary Add SSLSocket client/SSLEngine server to templates directory.32* @run main/othervm SSLSocketSSLEngineTemplate TLSv133* @run main/othervm SSLSocketSSLEngineTemplate TLSv1.134* @run main/othervm SSLSocketSSLEngineTemplate TLSv1.235* @run main/othervm SSLSocketSSLEngineTemplate TLSv1.336*/3738/**39* A SSLSocket/SSLEngine interop test case. This is not the way to40* code SSLEngine-based servers, but works for what we need to do here,41* which is to make sure that SSLEngine/SSLSockets can talk to each other.42* SSLEngines can use direct or indirect buffers, and different code43* is used to get at the buffer contents internally, so we test that here.44*45* The test creates one SSLSocket (client) and one SSLEngine (server).46* The SSLSocket talks to a raw ServerSocket, and the server code47* does the translation between byte [] and ByteBuffers that the SSLEngine48* can use. The "transport" layer consists of a Socket Input/OutputStream49* and two byte buffers for the SSLEngines: think of them50* as directly connected pipes.51*52* Again, this is a *very* simple example: real code will be much more53* involved. For example, different threading and I/O models could be54* used, transport mechanisms could close unexpectedly, and so on.55*56* When this application runs, notice that several messages57* (wrap/unwrap) pass before any application data is consumed or58* produced. (For more information, please see the SSL/TLS59* specifications.) There may several steps for a successful handshake,60* so it's typical to see the following series of operations:61*62* client server message63* ====== ====== =======64* write() ... ClientHello65* ... unwrap() ClientHello66* ... wrap() ServerHello/Certificate67* read() ... ServerHello/Certificate68* write() ... ClientKeyExchange69* write() ... ChangeCipherSpec70* write() ... Finished71* ... unwrap() ClientKeyExchange72* ... unwrap() ChangeCipherSpec73* ... unwrap() Finished74* ... wrap() ChangeCipherSpec75* ... wrap() Finished76* read() ... ChangeCipherSpec77* read() ... Finished78*/79import javax.net.ssl.*;80import javax.net.ssl.SSLEngineResult.*;81import java.io.*;82import java.net.*;83import java.security.*;84import java.nio.*;8586public class SSLSocketSSLEngineTemplate {8788/*89* Enables logging of the SSL/TLS operations.90*/91private static final boolean logging = true;9293/*94* Enables the JSSE system debugging system property:95*96* -Djavax.net.debug=all97*98* This gives a lot of low-level information about operations underway,99* including specific handshake messages, and might be best examined100* after gaining some familiarity with this application.101*/102private static final boolean debug = false;103private final SSLContext sslc;104private SSLEngine serverEngine; // server-side SSLEngine105private SSLSocket clientSocket;106107private final byte[] serverMsg =108"Hi there Client, I'm a Server.".getBytes();109private final byte[] clientMsg =110"Hello Server, I'm a Client! Pleased to meet you!".getBytes();111112private ByteBuffer serverOut; // write side of serverEngine113private ByteBuffer serverIn; // read side of serverEngine114115private volatile Exception clientException;116private volatile Exception serverException;117118/*119* For data transport, this example uses local ByteBuffers.120*/121private ByteBuffer cTOs; // "reliable" transport client->server122private ByteBuffer sTOc; // "reliable" transport server->client123124/*125* The following is to set up the keystores/trust material.126*/127private static final String pathToStores = "../etc";128private static final String keyStoreFile = "keystore";129private static final String trustStoreFile = "truststore";130private static final String keyFilename =131System.getProperty("test.src", ".") + "/" + pathToStores132+ "/" + keyStoreFile;133private static final String trustFilename =134System.getProperty("test.src", ".") + "/" + pathToStores135+ "/" + trustStoreFile;136137/*138* Main entry point for this test.139*/140public static void main(String args[]) throws Exception {141String protocol = args[0];142143// reset security properties to make sure that the algorithms144// and keys used in this test are not disabled.145Security.setProperty("jdk.tls.disabledAlgorithms", "");146Security.setProperty("jdk.certpath.disabledAlgorithms", "");147148if (debug) {149System.setProperty("javax.net.debug", "all");150}151152/*153* Run the tests with direct and indirect buffers.154*/155SSLSocketSSLEngineTemplate test =156new SSLSocketSSLEngineTemplate(protocol);157log("-------------------------------------");158log("Testing " + protocol + " for direct buffers ...");159test.runTest(true);160161log("---------------------------------------");162log("Testing " + protocol + " for indirect buffers ...");163test.runTest(false);164165log("Test Passed.");166}167168/*169* Create an initialized SSLContext to use for these tests.170*/171public SSLSocketSSLEngineTemplate(String protocol) throws Exception {172173KeyStore ks = KeyStore.getInstance("JKS");174KeyStore ts = KeyStore.getInstance("JKS");175176char[] passphrase = "passphrase".toCharArray();177178try (FileInputStream keyFile = new FileInputStream(keyFilename);179FileInputStream trustFile = new FileInputStream(trustFilename)) {180ks.load(keyFile, passphrase);181ts.load(trustFile, passphrase);182}183184KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");185kmf.init(ks, passphrase);186187TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");188tmf.init(ts);189190SSLContext sslCtx = SSLContext.getInstance(protocol);191192sslCtx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);193194sslc = sslCtx;195}196197/*198* Run the test.199*200* Sit in a tight loop, with the server engine calling wrap/unwrap201* regardless of whether data is available or not. We do this until202* we get the application data. Then we shutdown and go to the next one.203*204* The main loop handles all of the I/O phases of the SSLEngine's205* lifetime:206*207* initial handshaking208* application data transfer209* engine closing210*211* One could easily separate these phases into separate212* sections of code.213*/214private void runTest(boolean direct) throws Exception {215clientSocket = null;216boolean serverClose = direct;217218// generates the server-side Socket219try (ServerSocket serverSocket = new ServerSocket()) {220serverSocket.setReuseAddress(false);221serverSocket.bind(null);222int port = serverSocket.getLocalPort();223log("Port: " + port);224Thread thread = createClientThread(port, serverClose);225226createSSLEngine();227createBuffers(direct);228229// server-side socket that will read230try (Socket socket = serverSocket.accept()) {231socket.setSoTimeout(500);232233boolean closed = false;234// will try to read one more time in case client message235// is fragmented to multiple pieces236boolean retry = true;237238InputStream is = socket.getInputStream();239OutputStream os = socket.getOutputStream();240241SSLEngineResult serverResult; // results from last operation242243/*244* Examining the SSLEngineResults could be much more involved,245* and may alter the overall flow of the application.246*247* For example, if we received a BUFFER_OVERFLOW when trying248* to write to the output pipe, we could reallocate a larger249* pipe, but instead we wait for the peer to drain it.250*/251byte[] inbound = new byte[8192];252byte[] outbound = new byte[8192];253254while (!isEngineClosed(serverEngine)) {255int len;256257// Inbound data258log("================");259260// Read from the Client side.261try {262len = is.read(inbound);263if (len == -1) {264logSocketStatus(clientSocket);265if (clientSocket.isClosed()266|| clientSocket.isOutputShutdown()) {267log("Client socket was closed or shutdown output");268break;269} else {270throw new Exception("Unexpected EOF");271}272}273cTOs.put(inbound, 0, len);274} catch (SocketTimeoutException ste) {275// swallow. Nothing yet, probably waiting on us.276}277278cTOs.flip();279280serverResult = serverEngine.unwrap(cTOs, serverIn);281log("server unwrap: ", serverResult);282runDelegatedTasks(serverResult, serverEngine);283cTOs.compact();284285// Outbound data286log("----");287288serverResult = serverEngine.wrap(serverOut, sTOc);289log("server wrap: ", serverResult);290runDelegatedTasks(serverResult, serverEngine);291292sTOc.flip();293294if ((len = sTOc.remaining()) != 0) {295sTOc.get(outbound, 0, len);296os.write(outbound, 0, len);297// Give the other side a chance to process298}299300sTOc.compact();301302if (!closed && (serverOut.remaining() == 0)) {303closed = true;304305/*306* We'll alternate initiatating the shutdown.307* When the server initiates, it will take one more308* loop, but tests the orderly shutdown.309*/310if (serverClose) {311serverEngine.closeOutbound();312}313serverIn.flip();314315/*316* A sanity check to ensure we got what was sent.317*/318if (serverIn.remaining() != clientMsg.length) {319if (retry &&320serverIn.remaining() < clientMsg.length) {321log("Need to read more from client");322serverIn.compact();323retry = false;324continue;325} else {326throw new Exception(327"Client: Data length error");328}329}330331for (int i = 0; i < clientMsg.length; i++) {332if (clientMsg[i] != serverIn.get()) {333throw new Exception(334"Client: Data content error");335}336}337serverIn.compact();338}339}340} catch (Exception e) {341serverException = e;342} finally {343// Wait for the client to join up with us.344if (thread != null) {345thread.join();346}347}348} finally {349if (serverException != null) {350if (clientException != null) {351serverException.initCause(clientException);352}353throw serverException;354}355if (clientException != null) {356if (serverException != null) {357clientException.initCause(serverException);358}359throw clientException;360}361}362}363364/*365* Create a client thread which does simple SSLSocket operations.366* We'll write and read one data packet.367*/368private Thread createClientThread(final int port,369final boolean serverClose) throws Exception {370371Thread t = new Thread("ClientThread") {372373@Override374public void run() {375// client-side socket376try (SSLSocket sslSocket = (SSLSocket)sslc.getSocketFactory().377createSocket("localhost", port)) {378clientSocket = sslSocket;379380OutputStream os = sslSocket.getOutputStream();381InputStream is = sslSocket.getInputStream();382383// write(byte[]) goes in one shot.384os.write(clientMsg);385386byte[] inbound = new byte[2048];387int pos = 0;388389int len;390while ((len = is.read(inbound, pos, 2048 - pos)) != -1) {391pos += len;392// Let the client do the closing.393if ((pos == serverMsg.length) && !serverClose) {394sslSocket.close();395break;396}397}398399if (pos != serverMsg.length) {400throw new Exception("Client: Data length error");401}402403for (int i = 0; i < serverMsg.length; i++) {404if (inbound[i] != serverMsg[i]) {405throw new Exception("Client: Data content error");406}407}408} catch (Exception e) {409clientException = e;410}411}412};413t.start();414return t;415}416417/*418* Using the SSLContext created during object creation,419* create/configure the SSLEngines we'll use for this test.420*/421private void createSSLEngine() throws Exception {422/*423* Configure the serverEngine to act as a server in the SSL/TLS424* handshake.425*/426serverEngine = sslc.createSSLEngine();427serverEngine.setUseClientMode(false);428serverEngine.getNeedClientAuth();429}430431/*432* Create and size the buffers appropriately.433*/434private void createBuffers(boolean direct) {435436SSLSession session = serverEngine.getSession();437int appBufferMax = session.getApplicationBufferSize();438int netBufferMax = session.getPacketBufferSize();439440/*441* We'll make the input buffers a bit bigger than the max needed442* size, so that unwrap()s following a successful data transfer443* won't generate BUFFER_OVERFLOWS.444*445* We'll use a mix of direct and indirect ByteBuffers for446* tutorial purposes only. In reality, only use direct447* ByteBuffers when they give a clear performance enhancement.448*/449if (direct) {450serverIn = ByteBuffer.allocateDirect(appBufferMax + 50);451cTOs = ByteBuffer.allocateDirect(netBufferMax);452sTOc = ByteBuffer.allocateDirect(netBufferMax);453} else {454serverIn = ByteBuffer.allocate(appBufferMax + 50);455cTOs = ByteBuffer.allocate(netBufferMax);456sTOc = ByteBuffer.allocate(netBufferMax);457}458459serverOut = ByteBuffer.wrap(serverMsg);460}461462/*463* If the result indicates that we have outstanding tasks to do,464* go ahead and run them in this thread.465*/466private static void runDelegatedTasks(SSLEngineResult result,467SSLEngine engine) throws Exception {468469if (result.getHandshakeStatus() == HandshakeStatus.NEED_TASK) {470Runnable runnable;471while ((runnable = engine.getDelegatedTask()) != null) {472log("\trunning delegated task...");473runnable.run();474}475HandshakeStatus hsStatus = engine.getHandshakeStatus();476if (hsStatus == HandshakeStatus.NEED_TASK) {477throw new Exception(478"handshake shouldn't need additional tasks");479}480log("\tnew HandshakeStatus: " + hsStatus);481}482}483484private static boolean isEngineClosed(SSLEngine engine) {485return (engine.isOutboundDone() && engine.isInboundDone());486}487488private static void logSocketStatus(Socket socket) {489log("##### " + socket + " #####");490log("isBound: " + socket.isBound());491log("isConnected: " + socket.isConnected());492log("isClosed: " + socket.isClosed());493log("isInputShutdown: " + socket.isInputShutdown());494log("isOutputShutdown: " + socket.isOutputShutdown());495}496497/*498* Logging code499*/500private static boolean resultOnce = true;501502private static void log(String str, SSLEngineResult result) {503if (!logging) {504return;505}506if (resultOnce) {507resultOnce = false;508log("The format of the SSLEngineResult is: \n"509+ "\t\"getStatus() / getHandshakeStatus()\" +\n"510+ "\t\"bytesConsumed() / bytesProduced()\"\n");511}512HandshakeStatus hsStatus = result.getHandshakeStatus();513log(str514+ result.getStatus() + "/" + hsStatus + ", "515+ result.bytesConsumed() + "/" + result.bytesProduced()516+ " bytes");517if (hsStatus == HandshakeStatus.FINISHED) {518log("\t...ready for application data");519}520}521522private static void log(String str) {523if (logging) {524if (debug) {525System.err.println(str);526} else {527System.out.println(str);528}529}530}531}532533534