Path: blob/master/test/jdk/sun/security/ssl/ClientHandshaker/LengthCheckTest.java
41152 views
/*1* Copyright (c) 2015, 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 804486026* @summary Vectors and fixed length fields should be verified27* for allowed sizes.28* @library /test/lib29* @modules java.base/sun.security.ssl30* @run main/othervm LengthCheckTest31* @key randomness32*/3334/**35* A SSLEngine usage example which simplifies the presentation36* by removing the I/O and multi-threading concerns.37*38* The test creates two SSLEngines, simulating a client and server.39* The "transport" layer consists two byte buffers: think of them40* as directly connected pipes.41*42* Note, this is a *very* simple example: real code will be much more43* involved. For example, different threading and I/O models could be44* used, transport mechanisms could close unexpectedly, and so on.45*46* When this application runs, notice that several messages47* (wrap/unwrap) pass before any application data is consumed or48* produced. (For more information, please see the SSL/TLS49* specifications.) There may several steps for a successful handshake,50* so it's typical to see the following series of operations:51*52* client server message53* ====== ====== =======54* wrap() ... ClientHello55* ... unwrap() ClientHello56* ... wrap() ServerHello/Certificate57* unwrap() ... ServerHello/Certificate58* wrap() ... ClientKeyExchange59* wrap() ... ChangeCipherSpec60* wrap() ... Finished61* ... unwrap() ClientKeyExchange62* ... unwrap() ChangeCipherSpec63* ... unwrap() Finished64* ... wrap() ChangeCipherSpec65* ... wrap() Finished66* unwrap() ... ChangeCipherSpec67* unwrap() ... Finished68*/6970import javax.net.ssl.*;71import javax.net.ssl.SSLEngineResult.*;72import java.io.*;73import java.security.*;74import java.nio.*;75import java.util.List;76import java.util.ArrayList;77import java.util.Iterator;7879import jdk.test.lib.security.SecurityUtils;8081public class LengthCheckTest {8283/*84* Enables logging of the SSLEngine operations.85*/86private static final boolean logging = true;8788/*89* Enables the JSSE system debugging system property:90*91* -Djavax.net.debug=all92*93* This gives a lot of low-level information about operations underway,94* including specific handshake messages, and might be best examined95* after gaining some familiarity with this application.96*/97private static final boolean debug = false;98private static final boolean dumpBufs = true;99100private final SSLContext sslc;101102private SSLEngine clientEngine; // client Engine103private ByteBuffer clientOut; // write side of clientEngine104private ByteBuffer clientIn; // read side of clientEngine105106private SSLEngine serverEngine; // server Engine107private ByteBuffer serverOut; // write side of serverEngine108private ByteBuffer serverIn; // read side of serverEngine109110private HandshakeTest handshakeTest;111112/*113* For data transport, this example uses local ByteBuffers. This114* isn't really useful, but the purpose of this example is to show115* SSLEngine concepts, not how to do network transport.116*/117private ByteBuffer cTOs; // "reliable" transport client->server118private ByteBuffer sTOc; // "reliable" transport server->client119120/*121* The following is to set up the keystores.122*/123private static final String pathToStores = "../../../../javax/net/ssl/etc";124private static final String keyStoreFile = "keystore";125private static final String trustStoreFile = "truststore";126private static final String passwd = "passphrase";127128private static final String keyFilename =129System.getProperty("test.src", ".") + "/" + pathToStores +130"/" + keyStoreFile;131private static final String trustFilename =132System.getProperty("test.src", ".") + "/" + pathToStores +133"/" + trustStoreFile;134135// Define a few basic TLS record and message types we might need136private static final int TLS_RECTYPE_CCS = 0x14;137private static final int TLS_RECTYPE_ALERT = 0x15;138private static final int TLS_RECTYPE_HANDSHAKE = 0x16;139private static final int TLS_RECTYPE_APPDATA = 0x17;140141private static final int TLS_HS_HELLO_REQUEST = 0x00;142private static final int TLS_HS_CLIENT_HELLO = 0x01;143private static final int TLS_HS_SERVER_HELLO = 0x02;144private static final int TLS_HS_CERTIFICATE = 0x0B;145private static final int TLS_HS_SERVER_KEY_EXCHG = 0x0C;146private static final int TLS_HS_CERT_REQUEST = 0x0D;147private static final int TLS_HS_SERVER_HELLO_DONE = 0x0E;148private static final int TLS_HS_CERT_VERIFY = 0x0F;149private static final int TLS_HS_CLIENT_KEY_EXCHG = 0x10;150private static final int TLS_HS_FINISHED = 0x14;151152// We're not going to define all the alert types in TLS, just153// the ones we think we'll need to reference by name.154private static final int TLS_ALERT_LVL_WARNING = 0x01;155private static final int TLS_ALERT_LVL_FATAL = 0x02;156157private static final int TLS_ALERT_UNEXPECTED_MSG = 0x0A;158private static final int TLS_ALERT_HANDSHAKE_FAILURE = 0x28;159private static final int TLS_ALERT_INTERNAL_ERROR = 0x50;160private static final int TLS_ALERT_ILLEGAL_PARAMETER = 0x2F;161162public interface HandshakeTest {163void execTest() throws Exception;164}165166public final HandshakeTest servSendLongID = new HandshakeTest() {167@Override168public void execTest() throws Exception {169boolean gotException = false;170SSLEngineResult clientResult; // results from client's last op171SSLEngineResult serverResult; // results from server's last op172173log("\n==== Test: Client receives 64-byte session ID ====");174175// Send Client Hello176clientResult = clientEngine.wrap(clientOut, cTOs);177log("client wrap: ", clientResult);178runDelegatedTasks(clientResult, clientEngine);179cTOs.flip();180dumpByteBuffer("CLIENT-TO-SERVER", cTOs);181182// Server consumes Client Hello183serverResult = serverEngine.unwrap(cTOs, serverIn);184log("server unwrap: ", serverResult);185runDelegatedTasks(serverResult, serverEngine);186cTOs.compact();187188// Server generates ServerHello/Cert/Done record189serverResult = serverEngine.wrap(serverOut, sTOc);190log("server wrap: ", serverResult);191runDelegatedTasks(serverResult, serverEngine);192sTOc.flip();193194// Intercept the ServerHello messages and instead send195// one that has a 64-byte session ID.196if (isTlsMessage(sTOc, TLS_RECTYPE_HANDSHAKE,197TLS_HS_SERVER_HELLO)) {198ArrayList<ByteBuffer> recList = splitRecord(sTOc);199200// Use the original ServerHello as a template to craft one201// with a longer-than-allowed session ID.202ByteBuffer servHelloBuf =203createEvilServerHello(recList.get(0), 64);204205recList.set(0, servHelloBuf);206207// Now send each ByteBuffer (each being a complete208// TLS record) into the client-side unwrap.209// for (ByteBuffer bBuf : recList) {210211Iterator<ByteBuffer> iter = recList.iterator();212while (!gotException && (iter.hasNext())) {213ByteBuffer bBuf = iter.next();214dumpByteBuffer("SERVER-TO-CLIENT", bBuf);215try {216clientResult = clientEngine.unwrap(bBuf, clientIn);217} catch (SSLProtocolException e) {218log("Received expected SSLProtocolException: " + e);219gotException = true;220}221log("client unwrap: ", clientResult);222runDelegatedTasks(clientResult, clientEngine);223}224} else {225dumpByteBuffer("SERVER-TO-CLIENT", sTOc);226log("client unwrap: ", clientResult);227runDelegatedTasks(clientResult, clientEngine);228}229sTOc.compact();230231// The Client should now send a TLS Alert232clientResult = clientEngine.wrap(clientOut, cTOs);233log("client wrap: ", clientResult);234runDelegatedTasks(clientResult, clientEngine);235cTOs.flip();236dumpByteBuffer("CLIENT-TO-SERVER", cTOs);237238// At this point we can verify that both an exception239// was thrown and the proper action (a TLS alert) was240// sent back to the server.241if (gotException == false ||242!isTlsMessage(cTOs, TLS_RECTYPE_ALERT, TLS_ALERT_LVL_FATAL,243TLS_ALERT_ILLEGAL_PARAMETER)) {244throw new SSLException(245"Client failed to throw Alert:fatal:internal_error");246}247}248};249250public final HandshakeTest clientSendLongID = new HandshakeTest() {251@Override252public void execTest() throws Exception {253boolean gotException = false;254SSLEngineResult clientResult; // results from client's last op255SSLEngineResult serverResult; // results from server's last op256257log("\n==== Test: Server receives 64-byte session ID ====");258259// Send Client Hello260ByteBuffer evilClientHello = createEvilClientHello(64);261dumpByteBuffer("CLIENT-TO-SERVER", evilClientHello);262263// Server consumes Client Hello264serverResult = serverEngine.unwrap(evilClientHello, serverIn);265log("server unwrap: ", serverResult);266runDelegatedTasks(serverResult, serverEngine);267evilClientHello.compact();268269// Under normal circumstances this should be a ServerHello270// But should throw an exception instead due to the invalid271// session ID.272try {273serverResult = serverEngine.wrap(serverOut, sTOc);274log("server wrap: ", serverResult);275runDelegatedTasks(serverResult, serverEngine);276} catch (SSLProtocolException ssle) {277log("Received expected SSLProtocolException: " + ssle);278gotException = true;279}280281// We expect to see the server generate an alert here282serverResult = serverEngine.wrap(serverOut, sTOc);283log("server wrap: ", serverResult);284runDelegatedTasks(serverResult, serverEngine);285sTOc.flip();286dumpByteBuffer("SERVER-TO-CLIENT", sTOc);287288// At this point we can verify that both an exception289// was thrown and the proper action (a TLS alert) was290// sent back to the client.291if (gotException == false ||292!isTlsMessage(sTOc, TLS_RECTYPE_ALERT, TLS_ALERT_LVL_FATAL,293TLS_ALERT_ILLEGAL_PARAMETER)) {294throw new SSLException(295"Server failed to throw Alert:fatal:internal_error");296}297}298};299300301/*302* Main entry point for this test.303*/304public static void main(String args[]) throws Exception {305// Re-enable TLSv1 since test depends on it.306SecurityUtils.removeFromDisabledTlsAlgs("TLSv1");307308List<LengthCheckTest> ccsTests = new ArrayList<>();309310if (debug) {311System.setProperty("javax.net.debug", "ssl");312}313314ccsTests.add(new LengthCheckTest("ServSendLongID"));315ccsTests.add(new LengthCheckTest("ClientSendLongID"));316317for (LengthCheckTest test : ccsTests) {318test.runTest();319}320321System.out.println("Test Passed.");322}323324/*325* Create an initialized SSLContext to use for these tests.326*/327public LengthCheckTest(String testName) throws Exception {328329KeyStore ks = KeyStore.getInstance("JKS");330KeyStore ts = KeyStore.getInstance("JKS");331332char[] passphrase = "passphrase".toCharArray();333334ks.load(new FileInputStream(keyFilename), passphrase);335ts.load(new FileInputStream(trustFilename), passphrase);336337KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");338kmf.init(ks, passphrase);339340TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");341tmf.init(ts);342343SSLContext sslCtx = SSLContext.getInstance("TLS");344345sslCtx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);346347sslc = sslCtx;348349switch (testName) {350case "ServSendLongID":351handshakeTest = servSendLongID;352break;353case "ClientSendLongID":354handshakeTest = clientSendLongID;355break;356default:357throw new IllegalArgumentException("Unknown test name: " +358testName);359}360}361362/*363* Run the test.364*365* Sit in a tight loop, both engines calling wrap/unwrap regardless366* of whether data is available or not. We do this until both engines367* report back they are closed.368*369* The main loop handles all of the I/O phases of the SSLEngine's370* lifetime:371*372* initial handshaking373* application data transfer374* engine closing375*376* One could easily separate these phases into separate377* sections of code.378*/379private void runTest() throws Exception {380boolean dataDone = false;381382createSSLEngines();383createBuffers();384385handshakeTest.execTest();386}387388/*389* Using the SSLContext created during object creation,390* create/configure the SSLEngines we'll use for this test.391*/392private void createSSLEngines() throws Exception {393/*394* Configure the serverEngine to act as a server in the SSL/TLS395* handshake. Also, require SSL client authentication.396*/397serverEngine = sslc.createSSLEngine();398serverEngine.setUseClientMode(false);399serverEngine.setNeedClientAuth(false);400401/*402* Similar to above, but using client mode instead.403*/404clientEngine = sslc.createSSLEngine("client", 80);405clientEngine.setUseClientMode(true);406407// In order to make a test that will be backwards compatible408// going back to JDK 5, force the handshake to be TLS 1.0 and409// use one of the older cipher suites.410clientEngine.setEnabledProtocols(new String[]{"TLSv1"});411clientEngine.setEnabledCipherSuites(412new String[]{"TLS_RSA_WITH_AES_128_CBC_SHA"});413}414415/*416* Create and size the buffers appropriately.417*/418private void createBuffers() {419420/*421* We'll assume the buffer sizes are the same422* between client and server.423*/424SSLSession session = clientEngine.getSession();425int appBufferMax = session.getApplicationBufferSize();426int netBufferMax = session.getPacketBufferSize();427428/*429* We'll make the input buffers a bit bigger than the max needed430* size, so that unwrap()s following a successful data transfer431* won't generate BUFFER_OVERFLOWS.432*433* We'll use a mix of direct and indirect ByteBuffers for434* tutorial purposes only. In reality, only use direct435* ByteBuffers when they give a clear performance enhancement.436*/437clientIn = ByteBuffer.allocate(appBufferMax + 50);438serverIn = ByteBuffer.allocate(appBufferMax + 50);439440cTOs = ByteBuffer.allocateDirect(netBufferMax);441sTOc = ByteBuffer.allocateDirect(netBufferMax);442443clientOut = ByteBuffer.wrap("Hi Server, I'm Client".getBytes());444serverOut = ByteBuffer.wrap("Hello Client, I'm Server".getBytes());445}446447/*448* If the result indicates that we have outstanding tasks to do,449* go ahead and run them in this thread.450*/451private static void runDelegatedTasks(SSLEngineResult result,452SSLEngine engine) throws Exception {453454if (result.getHandshakeStatus() == HandshakeStatus.NEED_TASK) {455Runnable runnable;456while ((runnable = engine.getDelegatedTask()) != null) {457log("\trunning delegated task...");458runnable.run();459}460HandshakeStatus hsStatus = engine.getHandshakeStatus();461if (hsStatus == HandshakeStatus.NEED_TASK) {462throw new Exception(463"handshake shouldn't need additional tasks");464}465log("\tnew HandshakeStatus: " + hsStatus);466}467}468469private static boolean isEngineClosed(SSLEngine engine) {470return (engine.isOutboundDone() && engine.isInboundDone());471}472473/*474* Simple check to make sure everything came across as expected.475*/476private static void checkTransfer(ByteBuffer a, ByteBuffer b)477throws Exception {478a.flip();479b.flip();480481if (!a.equals(b)) {482throw new Exception("Data didn't transfer cleanly");483} else {484log("\tData transferred cleanly");485}486487a.position(a.limit());488b.position(b.limit());489a.limit(a.capacity());490b.limit(b.capacity());491}492493/*494* Logging code495*/496private static boolean resultOnce = true;497498private static void log(String str, SSLEngineResult result) {499if (!logging) {500return;501}502if (resultOnce) {503resultOnce = false;504System.out.println("The format of the SSLEngineResult is: \n" +505"\t\"getStatus() / getHandshakeStatus()\" +\n" +506"\t\"bytesConsumed() / bytesProduced()\"\n");507}508HandshakeStatus hsStatus = result.getHandshakeStatus();509log(str +510result.getStatus() + "/" + hsStatus + ", " +511result.bytesConsumed() + "/" + result.bytesProduced() +512" bytes");513if (hsStatus == HandshakeStatus.FINISHED) {514log("\t...ready for application data");515}516}517518private static void log(String str) {519if (logging) {520System.out.println(str);521}522}523524/**525* Split a record consisting of multiple TLS handshake messages526* into individual TLS records, each one in a ByteBuffer of its own.527*528* @param tlsRecord A ByteBuffer containing the tls record data.529* The position of the buffer should be at the first byte530* in the TLS record data.531*532* @return An ArrayList consisting of one or more ByteBuffers. Each533* ByteBuffer will contain a single TLS record with one message.534* That message will be taken from the input record. The order535* of the messages in the ArrayList will be the same as they536* were in the input record.537*/538private ArrayList<ByteBuffer> splitRecord(ByteBuffer tlsRecord) {539SSLSession session = clientEngine.getSession();540int netBufferMax = session.getPacketBufferSize();541ArrayList<ByteBuffer> recordList = new ArrayList<>();542543if (tlsRecord.hasRemaining()) {544int type = Byte.toUnsignedInt(tlsRecord.get());545byte ver_major = tlsRecord.get();546byte ver_minor = tlsRecord.get();547int recLen = Short.toUnsignedInt(tlsRecord.getShort());548byte[] newMsgData = null;549while (tlsRecord.hasRemaining()) {550ByteBuffer newRecord = ByteBuffer.allocateDirect(netBufferMax);551switch (type) {552case TLS_RECTYPE_CCS:553case TLS_RECTYPE_ALERT:554case TLS_RECTYPE_APPDATA:555// None of our tests have multiple non-handshake556// messages coalesced into a single record.557break;558case TLS_RECTYPE_HANDSHAKE:559newMsgData = getHandshakeMessage(tlsRecord);560break;561}562563// Put a new TLS record on the destination ByteBuffer564newRecord.put((byte)type);565newRecord.put(ver_major);566newRecord.put(ver_minor);567newRecord.putShort((short)newMsgData.length);568569// Now add the message content itself and attach to the570// returned ArrayList571newRecord.put(newMsgData);572newRecord.flip();573recordList.add(newRecord);574}575}576577return recordList;578}579580private static ByteBuffer createEvilClientHello(int sessIdLen) {581ByteBuffer newRecord = ByteBuffer.allocateDirect(4096);582583// Lengths will initially be place holders until we determine the584// finished length of the ByteBuffer. Then we'll go back and scribble585// in the correct lengths.586587newRecord.put((byte)TLS_RECTYPE_HANDSHAKE); // Record type588newRecord.putShort((short)0x0301); // Protocol (TLS 1.0)589newRecord.putShort((short)0); // Length place holder590591newRecord.putInt(TLS_HS_CLIENT_HELLO << 24); // HS type and length592newRecord.putShort((short)0x0301);593newRecord.putInt((int)(System.currentTimeMillis() / 1000));594SecureRandom sr = new SecureRandom();595byte[] randBuf = new byte[28];596sr.nextBytes(randBuf);597newRecord.put(randBuf); // Client Random598newRecord.put((byte)sessIdLen); // Session ID length599if (sessIdLen > 0) {600byte[] sessId = new byte[sessIdLen];601sr.nextBytes(sessId);602newRecord.put(sessId); // Session ID603}604newRecord.putShort((short)2); // 2 bytes of ciphers605newRecord.putShort((short)0x002F); // TLS_RSA_AES_CBC_SHA606newRecord.putShort((short)0x0100); // only null compression607newRecord.putShort((short)5); // 5 bytes of extensions608newRecord.putShort((short)0xFF01); // Renegotiation info609newRecord.putShort((short)1);610newRecord.put((byte)0); // No reneg info exts611612// Go back and fill in the correct length values for the record613// and handshake message headers.614int recordLength = newRecord.position();615newRecord.putShort(3, (short)(recordLength - 5));616int newTypeAndLen = (newRecord.getInt(5) & 0xFF000000) |617((recordLength - 9) & 0x00FFFFFF);618newRecord.putInt(5, newTypeAndLen);619620newRecord.flip();621return newRecord;622}623624private static ByteBuffer createEvilServerHello(ByteBuffer origHello,625int newSessIdLen) {626if (newSessIdLen < 0 || newSessIdLen > Byte.MAX_VALUE) {627throw new RuntimeException("Length must be 0 <= X <= 127");628}629630ByteBuffer newRecord = ByteBuffer.allocateDirect(4096);631// Copy the bytes from the old hello to the new up to the session ID632// field. We will go back later and fill in a new length field in633// the record header. This includes the record header (5 bytes), the634// Handshake message header (4 bytes), protocol version (2 bytes),635// and the random (32 bytes).636ByteBuffer scratchBuffer = origHello.slice();637scratchBuffer.limit(43);638newRecord.put(scratchBuffer);639640// Advance the position in the originial hello buffer past the641// session ID.642origHello.position(43);643int origIDLen = Byte.toUnsignedInt(origHello.get());644if (origIDLen > 0) {645// Skip over the session ID646origHello.position(origHello.position() + origIDLen);647}648649// Now add our own sessionID to the new record650SecureRandom sr = new SecureRandom();651byte[] sessId = new byte[newSessIdLen];652sr.nextBytes(sessId);653newRecord.put((byte)newSessIdLen);654newRecord.put(sessId);655656// Create another slice in the original buffer, based on the position657// past the session ID. Copy the remaining bytes into the new658// hello buffer. Then go back and fix up the length659newRecord.put(origHello.slice());660661// Go back and fill in the correct length values for the record662// and handshake message headers.663int recordLength = newRecord.position();664newRecord.putShort(3, (short)(recordLength - 5));665int newTypeAndLen = (newRecord.getInt(5) & 0xFF000000) |666((recordLength - 9) & 0x00FFFFFF);667newRecord.putInt(5, newTypeAndLen);668669newRecord.flip();670return newRecord;671}672673/**674* Look at an incoming TLS record and see if it is the desired675* record type, and where appropriate the correct subtype.676*677* @param srcRecord The input TLS record to be evaluated. This678* method will only look at the leading message if multiple679* TLS handshake messages are coalesced into a single record.680* @param reqRecType The requested TLS record type681* @param recParams Zero or more integer sub type fields. For CCS682* and ApplicationData, no params are used. For handshake records,683* one value corresponding to the HandshakeType is required.684* For Alerts, two values corresponding to AlertLevel and685* AlertDescription are necessary.686*687* @return true if the proper handshake message is the first one688* in the input record, false otherwise.689*/690private boolean isTlsMessage(ByteBuffer srcRecord, int reqRecType,691int... recParams) {692boolean foundMsg = false;693694if (srcRecord.hasRemaining()) {695srcRecord.mark();696697// Grab the fields from the TLS Record698int recordType = Byte.toUnsignedInt(srcRecord.get());699byte ver_major = srcRecord.get();700byte ver_minor = srcRecord.get();701int recLen = Short.toUnsignedInt(srcRecord.getShort());702703if (recordType == reqRecType) {704// For any zero-length recParams, making sure the requested705// type is sufficient.706if (recParams.length == 0) {707foundMsg = true;708} else {709switch (recordType) {710case TLS_RECTYPE_CCS:711case TLS_RECTYPE_APPDATA:712// We really shouldn't find ourselves here, but713// if someone asked for these types and had more714// recParams we can ignore them.715foundMsg = true;716break;717case TLS_RECTYPE_ALERT:718// Needs two params, AlertLevel and AlertDescription719if (recParams.length != 2) {720throw new RuntimeException(721"Test for Alert requires level and desc.");722} else {723int level = Byte.toUnsignedInt(srcRecord.get());724int desc = Byte.toUnsignedInt(srcRecord.get());725if (level == recParams[0] &&726desc == recParams[1]) {727foundMsg = true;728}729}730break;731case TLS_RECTYPE_HANDSHAKE:732// Needs one parameter, HandshakeType733if (recParams.length != 1) {734throw new RuntimeException(735"Test for Handshake requires only HS type");736} else {737// Go into the first handhshake message in the738// record and grab the handshake message header.739// All we need to do is parse out the leading740// byte.741int msgHdr = srcRecord.getInt();742int msgType = (msgHdr >> 24) & 0x000000FF;743if (msgType == recParams[0]) {744foundMsg = true;745}746}747break;748}749}750}751752srcRecord.reset();753}754755return foundMsg;756}757758private byte[] getHandshakeMessage(ByteBuffer srcRecord) {759// At the start of this routine, the position should be lined up760// at the first byte of a handshake message. Mark this location761// so we can return to it after reading the type and length.762srcRecord.mark();763int msgHdr = srcRecord.getInt();764int type = (msgHdr >> 24) & 0x000000FF;765int length = msgHdr & 0x00FFFFFF;766767// Create a byte array that has enough space for the handshake768// message header and body.769byte[] data = new byte[length + 4];770srcRecord.reset();771srcRecord.get(data, 0, length + 4);772773return (data);774}775776/**777* Hex-dumps a ByteBuffer to stdout.778*/779private static void dumpByteBuffer(String header, ByteBuffer bBuf) {780if (dumpBufs == false) {781return;782}783784int bufLen = bBuf.remaining();785if (bufLen > 0) {786bBuf.mark();787788// We expect the position of the buffer to be at the789// beginning of a TLS record. Get the type, version and length.790int type = Byte.toUnsignedInt(bBuf.get());791int ver_major = Byte.toUnsignedInt(bBuf.get());792int ver_minor = Byte.toUnsignedInt(bBuf.get());793int recLen = Short.toUnsignedInt(bBuf.getShort());794795log("===== " + header + " (" + tlsRecType(type) + " / " +796ver_major + "." + ver_minor + " / " + bufLen + " bytes) =====");797bBuf.reset();798for (int i = 0; i < bufLen; i++) {799if (i != 0 && i % 16 == 0) {800System.out.print("\n");801}802System.out.format("%02X ", bBuf.get(i));803}804log("\n===============================================");805bBuf.reset();806}807}808809private static String tlsRecType(int type) {810switch (type) {811case 20:812return "Change Cipher Spec";813case 21:814return "Alert";815case 22:816return "Handshake";817case 23:818return "Application Data";819default:820return ("Unknown (" + type + ")");821}822}823}824825826