Path: blob/master/test/jdk/javax/net/ssl/SSLEngine/NoAuthClientAuth.java
41152 views
/*1* Copyright (c) 2003, 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 4495742 819049231* @summary Demonstrate SSLEngine switch from no client auth to client auth.32* @run main/othervm NoAuthClientAuth SSLv333* @run main/othervm NoAuthClientAuth TLSv134* @run main/othervm NoAuthClientAuth TLSv1.135* @run main/othervm NoAuthClientAuth TLSv1.236* @author Brad R. Wetmore37*/3839/**40* A SSLEngine usage example which simplifies the presentation41* by removing the I/O and multi-threading concerns.42*43* The test creates two SSLEngines, simulating a client and server.44* The "transport" layer consists two byte buffers: think of them45* as directly connected pipes.46*47* Note, this is a *very* simple example: real code will be much more48* involved. For example, different threading and I/O models could be49* used, transport mechanisms could close unexpectedly, and so on.50*51* When this application runs, notice that several messages52* (wrap/unwrap) pass before any application data is consumed or53* produced. (For more information, please see the SSL/TLS54* specifications.) There may several steps for a successful handshake,55* so it's typical to see the following series of operations:56*57* client server message58* ====== ====== =======59* wrap() ... ClientHello60* ... unwrap() ClientHello61* ... wrap() ServerHello/Certificate62* unwrap() ... ServerHello/Certificate63* wrap() ... ClientKeyExchange64* wrap() ... ChangeCipherSpec65* wrap() ... Finished66* ... unwrap() ClientKeyExchange67* ... unwrap() ChangeCipherSpec68* ... unwrap() Finished69* ... wrap() ChangeCipherSpec70* ... wrap() Finished71* unwrap() ... ChangeCipherSpec72* unwrap() ... Finished73*74* In this example, we do a rehandshake and make sure that completes75* correctly.76*/7778import javax.net.ssl.*;79import javax.net.ssl.SSLEngineResult.*;80import java.io.*;81import java.security.*;82import java.nio.*;8384// Note that this test case depends on JSSE provider implementation details.85public class NoAuthClientAuth {8687/*88* Enables logging of the SSLEngine operations.89*/90private static boolean logging = true;9192/*93* Enables the JSSE system debugging system property:94*95* -Djavax.net.debug=all96*97* This gives a lot of low-level information about operations underway,98* including specific handshake messages, and might be best examined99* after gaining some familiarity with this application.100*/101private static boolean debug = true;102103private SSLContext sslc;104105private SSLEngine clientEngine; // client Engine106private ByteBuffer clientOut; // write side of clientEngine107private ByteBuffer clientIn; // read side of clientEngine108109private SSLEngine serverEngine; // server Engine110private ByteBuffer serverOut; // write side of serverEngine111private ByteBuffer serverIn; // read side of serverEngine112113/*114* For data transport, this example uses local ByteBuffers. This115* isn't really useful, but the purpose of this example is to show116* SSLEngine concepts, not how to do network transport.117*/118private ByteBuffer cTOs; // "reliable" transport client->server119private ByteBuffer sTOc; // "reliable" transport server->client120121/*122* The following is to set up the keystores.123*/124private static String pathToStores = "../etc";125private static String keyStoreFile = "keystore";126private static String trustStoreFile = "truststore";127private static String passwd = "passphrase";128129private static String keyFilename =130System.getProperty("test.src", ".") + "/" + pathToStores +131"/" + keyStoreFile;132private static String trustFilename =133System.getProperty("test.src", ".") + "/" + pathToStores +134"/" + trustStoreFile;135// the specified protocol136private static String tlsProtocol;137138/*139* Main entry point for this test.140*/141public static void main(String args[]) throws Exception {142Security.setProperty("jdk.tls.disabledAlgorithms", "");143144if (debug) {145System.setProperty("javax.net.debug", "all");146}147148tlsProtocol = args[0];149150NoAuthClientAuth test = new NoAuthClientAuth();151test.runTest();152153System.out.println("Test Passed.");154}155156/*157* Create an initialized SSLContext to use for these tests.158*/159public NoAuthClientAuth() throws Exception {160161KeyStore ks = KeyStore.getInstance("JKS");162KeyStore ts = KeyStore.getInstance("JKS");163164char[] passphrase = "passphrase".toCharArray();165166ks.load(new FileInputStream(keyFilename), passphrase);167ts.load(new FileInputStream(trustFilename), passphrase);168169KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");170kmf.init(ks, passphrase);171172TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");173tmf.init(ts);174175SSLContext sslCtx = SSLContext.getInstance("TLS");176177sslCtx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);178179sslc = sslCtx;180}181182/*183* Run the test.184*185* Sit in a tight loop, both engines calling wrap/unwrap regardless186* of whether data is available or not. We do this until both engines187* report back they are closed.188*189* The main loop handles all of the I/O phases of the SSLEngine's190* lifetime:191*192* initial handshaking193* application data transfer194* engine closing195*196* One could easily separate these phases into separate197* sections of code.198*/199private void runTest() throws Exception {200201createSSLEngines();202createBuffers();203204SSLEngineResult clientResult; // results from client's last operation205SSLEngineResult serverResult; // results from server's last operation206207/*208* Examining the SSLEngineResults could be much more involved,209* and may alter the overall flow of the application.210*211* For example, if we received a BUFFER_OVERFLOW when trying212* to write to the output pipe, we could reallocate a larger213* pipe, but instead we wait for the peer to drain it.214*/215int hsCompleted = 0;216while (!isEngineClosed(clientEngine) ||217!isEngineClosed(serverEngine)) {218219log("================");220221clientResult = clientEngine.wrap(clientOut, cTOs);222log("client wrap: ", clientResult);223runDelegatedTasks(clientResult, clientEngine);224clientOut.rewind();225226serverResult = serverEngine.wrap(serverOut, sTOc);227log("server wrap: ", serverResult);228runDelegatedTasks(serverResult, serverEngine);229serverOut.rewind();230231// Jeanfrancois:232// Here is the main rehandshaking step.233if (serverResult.getHandshakeStatus() ==234HandshakeStatus.FINISHED) {235hsCompleted++;236log("\t" + hsCompleted + " handshake completed");237if (hsCompleted == 1) {238try {239serverEngine.getSession().getPeerCertificates();240throw new Exception("Should have got exception");241} catch (SSLPeerUnverifiedException e) {242System.out.println("Caught proper exception." + e);243}244log("\tInvalidating session, setting client auth, " +245" starting rehandshake");246serverEngine.getSession().invalidate();247serverEngine.setNeedClientAuth(true);248serverEngine.beginHandshake();249} else if (hsCompleted == 2) {250java.security.cert.Certificate [] certs =251serverEngine.getSession().getPeerCertificates();252System.out.println("Client Certificate(s) received");253for (java.security.cert.Certificate c : certs) {254System.out.println(c);255}256// log("Closing server.");257// serverEngine.closeOutbound();258} // nothing.259}260261cTOs.flip();262sTOc.flip();263264log("----");265266if (!clientEngine.isInboundDone()) {267clientResult = clientEngine.unwrap(sTOc, clientIn);268log("client unwrap: ", clientResult);269runDelegatedTasks(clientResult, clientEngine);270clientIn.clear();271sTOc.compact();272} else {273sTOc.clear();274}275276if (!serverEngine.isInboundDone()) {277serverResult = serverEngine.unwrap(cTOs, serverIn);278log("server unwrap: ", serverResult);279runDelegatedTasks(serverResult, serverEngine);280serverIn.clear();281cTOs.compact();282} else {283cTOs.clear();284}285286if (hsCompleted == 2) {287log("Closing server.");288serverEngine.closeOutbound();289}290}291}292293/*294* Using the SSLContext created during object creation,295* create/configure the SSLEngines we'll use for this test.296*/297private void createSSLEngines() throws Exception {298/*299* Configure the serverEngine to act as a server in the SSL/TLS300* handshake. Also, require SSL client authentication.301*/302serverEngine = sslc.createSSLEngine();303serverEngine.setUseClientMode(false);304serverEngine.setNeedClientAuth(false);305306// Enable all supported protocols on server side to test SSLv3307if ("SSLv3".equals(tlsProtocol)) {308serverEngine.setEnabledProtocols(serverEngine.getSupportedProtocols());309}310311/*312* Similar to above, but using client mode instead.313*/314clientEngine = sslc.createSSLEngine("client", 80);315clientEngine.setUseClientMode(true);316clientEngine.setEnabledProtocols(new String[] { tlsProtocol });317}318319/*320* Create and size the buffers appropriately.321*/322private void createBuffers() {323324/*325* We'll assume the buffer sizes are the same326* between client and server.327*/328SSLSession session = clientEngine.getSession();329int appBufferMax = session.getApplicationBufferSize();330int netBufferMax = session.getPacketBufferSize();331332/*333* We'll make the input buffers a bit bigger than the max needed334* size, so that unwrap()s following a successful data transfer335* won't generate BUFFER_OVERFLOWS.336*337* We'll use a mix of direct and indirect ByteBuffers for338* tutorial purposes only. In reality, only use direct339* ByteBuffers when they give a clear performance enhancement.340*/341clientIn = ByteBuffer.allocate(appBufferMax + 50);342serverIn = ByteBuffer.allocate(appBufferMax + 50);343344cTOs = ByteBuffer.allocateDirect(netBufferMax);345sTOc = ByteBuffer.allocateDirect(netBufferMax);346347clientOut = ByteBuffer.wrap("Hi Server, I'm Client".getBytes());348serverOut = ByteBuffer.wrap("Hello Client, I'm Server".getBytes());349}350351/*352* If the result indicates that we have outstanding tasks to do,353* go ahead and run them in this thread.354*/355private static void runDelegatedTasks(SSLEngineResult result,356SSLEngine engine) throws Exception {357358if (result.getHandshakeStatus() == HandshakeStatus.NEED_TASK) {359Runnable runnable;360while ((runnable = engine.getDelegatedTask()) != null) {361log("\trunning delegated task...");362runnable.run();363}364HandshakeStatus hsStatus = engine.getHandshakeStatus();365if (hsStatus == HandshakeStatus.NEED_TASK) {366throw new Exception(367"handshake shouldn't need additional tasks");368}369log("\tnew HandshakeStatus: " + hsStatus);370}371}372373private static boolean isEngineClosed(SSLEngine engine) {374return (engine.isOutboundDone() && engine.isInboundDone());375}376377/*378* Simple check to make sure everything came across as expected.379*/380private static void checkTransfer(ByteBuffer a, ByteBuffer b)381throws Exception {382a.flip();383b.flip();384385if (!a.equals(b)) {386throw new Exception("Data didn't transfer cleanly");387} else {388log("\tData transferred cleanly");389}390391a.position(a.limit());392b.position(b.limit());393a.limit(a.capacity());394b.limit(b.capacity());395}396397/*398* Logging code399*/400private static boolean resultOnce = true;401402private static void log(String str, SSLEngineResult result) {403if (!logging) {404return;405}406if (resultOnce) {407resultOnce = false;408System.out.println("The format of the SSLEngineResult is: \n" +409"\t\"getStatus() / getHandshakeStatus()\" +\n" +410"\t\"bytesConsumed() / bytesProduced()\"\n");411}412HandshakeStatus hsStatus = result.getHandshakeStatus();413log(str +414result.getStatus() + "/" + hsStatus + ", " +415result.bytesConsumed() + "/" + result.bytesProduced() +416" bytes");417if (hsStatus == HandshakeStatus.FINISHED) {418log("\t...ready for application data");419}420}421422private static void log(String str) {423if (logging) {424System.out.println(str);425}426}427}428429430