Created
September 15, 2015 14:38
-
-
Save benstopford/c32a26367374a7b3f6d4 to your computer and use it in GitHub Desktop.
Simple, hacky SSL Benchmark ported from oracle example
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package kafka.tools; | |
/* | |
* Copyright (c) 2004, Oracle and/or its affiliates. All rights reserved. | |
* | |
* Redistribution and use in source and binary forms, with or without | |
* modification, are permitted provided that the following conditions | |
* are met: | |
* | |
* -Redistribution of source code must retain the above copyright | |
* notice, this list of conditions and the following disclaimer. | |
* | |
* -Redistribution in binary form must reproduce the above copyright | |
* notice, this list of conditions and the following disclaimer in the | |
* documentation and/or other materials provided with the | |
* distribution. | |
* | |
* Neither the name of Oracle nor the names of | |
* contributors may be used to endorse or promote products derived from | |
* this software without specific prior written permission. | |
* | |
* This software is provided "AS IS," without a warranty of any kind. | |
* ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, | |
* INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A | |
* PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN | |
* MIDROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR | |
* ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR | |
* DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN | |
* OR ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR | |
* FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE | |
* DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, | |
* ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF | |
* SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. | |
* | |
* You acknowledge that this software is not designed, licensed or | |
* intended for use in the design, construction, operation or | |
* maintenance of any nuclear facility. | |
*/ | |
/** | |
* A SSLEngine usage example which simplifies the presentation | |
* by removing the I/O and multi-threading concerns. | |
* | |
* The demo creates two SSLEngines, simulating a client and server. | |
* The "transport" layer consists two ByteBuffers: think of them | |
* as directly connected pipes. | |
* | |
* Note, this is a *very* simple example: real code will be much more | |
* involved. For example, different threading and I/O models could be | |
* used, transport mechanisms could close unexpectedly, and so on. | |
* | |
* When this application runs, notice that several messages | |
* (wrap/unwrap) pass before any application data is consumed or | |
* produced. (For more information, please see the SSL/TLS | |
* specifications.) There may several steps for a successful handshake, | |
* so it's typical to see the following series of operations: | |
* | |
* client server message | |
* ====== ====== ======= | |
* wrap() ... ClientHello | |
* ... unwrap() ClientHello | |
* ... wrap() ServerHello/Certificate | |
* unwrap() ... ServerHello/Certificate | |
* wrap() ... ClientKeyExchange | |
* wrap() ... ChangeCipherSpec | |
* wrap() ... Finished | |
* ... unwrap() ClientKeyExchange | |
* ... unwrap() ChangeCipherSpec | |
* ... unwrap() Finished | |
* ... wrap() ChangeCipherSpec | |
* ... wrap() Finished | |
* unwrap() ... ChangeCipherSpec | |
* unwrap() ... Finished | |
*/ | |
import javax.net.ssl.*; | |
import javax.net.ssl.SSLEngineResult.*; | |
import java.io.*; | |
import java.security.*; | |
import java.nio.*; | |
public class SSLEngineSimpleDemo { | |
private static int dataTransferSize; | |
private static int netBufferSize; | |
/* | |
* Enables logging of the SSLEngine operations. | |
*/ | |
private static boolean logging = false; | |
/* | |
* Enables the JSSE system debugging system property: | |
* | |
* -Djavax.net.debug=all | |
* | |
* This gives a lot of low-level information about operations underway, | |
* including specific handshake messages, and might be best examined | |
* after gaining some familiarity with this application. | |
*/ | |
private static boolean debug = false; | |
private SSLContext sslc; | |
private SSLEngine clientEngine; // client Engine | |
private ByteBuffer clientOut; // write side of clientEngine | |
private ByteBuffer clientIn; // read side of clientEngine | |
private SSLEngine serverEngine; // server Engine | |
private ByteBuffer serverOut; // write side of serverEngine | |
private ByteBuffer serverIn; // read side of serverEngine | |
/* | |
* For data transport, this example uses local ByteBuffers. This | |
* isn't really useful, but the purpose of this example is to show | |
* SSLEngine concepts, not how to do network transport. | |
*/ | |
private ByteBuffer cTOs; // "reliable" transport client->server | |
private ByteBuffer sTOc; // "reliable" transport server->client | |
/* | |
* The following is to set up the keystores. | |
*/ | |
private static String keyStoreFile = "ssl-conf/keystore.jks"; | |
private static String trustStoreFile = "ssl-conf/truststore.jks"; | |
private static String passwd = "techno"; | |
/* | |
* Main entry point for this demo. | |
*/ | |
public static void main(String args[]) throws Exception { | |
if (debug) { | |
System.setProperty("javax.net.debug", "all"); | |
} | |
dataTransferSize = 10 * 1024 * 1024; | |
netBufferSize = 17 * 1024; //needs to be bigger than 16k | |
SSLEngineSimpleDemo demo = new SSLEngineSimpleDemo(); | |
for (int i = 0; i < 20; i++) | |
demo.runDemo(); | |
System.out.println("Demo Completed."); | |
} | |
/* | |
* Create an initialized SSLContext to use for this demo. | |
*/ | |
public SSLEngineSimpleDemo() throws Exception { | |
KeyStore ks = KeyStore.getInstance("JKS"); | |
KeyStore ts = KeyStore.getInstance("JKS"); | |
char[] passphrase = "techno".toCharArray(); | |
ks.load(new FileInputStream(keyStoreFile), passphrase); | |
ts.load(new FileInputStream(trustStoreFile), passphrase); | |
KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509"); | |
kmf.init(ks, passphrase); | |
TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509"); | |
tmf.init(ts); | |
SSLContext sslCtx = SSLContext.getInstance("TLS"); | |
sslCtx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null); | |
sslc = sslCtx; | |
} | |
/* | |
* Run the demo. | |
* | |
* Sit in a tight loop, both engines calling wrap/unwrap regardless | |
* of whether data is available or not. We do this until both engines | |
* report back they are closed. | |
* | |
* The main loop handles all of the I/O phases of the SSLEngine's | |
* lifetime: | |
* | |
* initial handshaking | |
* application data transfer | |
* engine closing | |
* | |
* One could easily separate these phases into separate | |
* sections of code. | |
*/ | |
private void runDemo() throws Exception { | |
long start = System.currentTimeMillis(); | |
boolean dataDone = false; | |
createSSLEngines(); | |
createBuffers(); | |
SSLEngineResult clientResult; // results from client's last operation | |
SSLEngineResult serverResult; // results from server's last operation | |
/* | |
* Examining the SSLEngineResults could be much more involved, | |
* and may alter the overall flow of the application. | |
* | |
* For example, if we received a BUFFER_OVERFLOW when trying | |
* to write to the output pipe, we could reallocate a larger | |
* pipe, but instead we wait for the peer to drain it. | |
*/ | |
while (!isEngineClosed(clientEngine) || | |
!isEngineClosed(serverEngine)) { | |
log("================"); | |
clientResult = clientEngine.wrap(clientOut, cTOs); | |
log("client wrap: ", clientResult); | |
runDelegatedTasks(clientResult, clientEngine); | |
serverResult = serverEngine.wrap(serverOut, sTOc); | |
log("server wrap: ", serverResult); | |
runDelegatedTasks(serverResult, serverEngine); | |
cTOs.flip(); | |
sTOc.flip(); | |
log("----"); | |
clientResult = clientEngine.unwrap(sTOc, clientIn); | |
log("client unwrap: ", clientResult); | |
runDelegatedTasks(clientResult, clientEngine); | |
serverResult = serverEngine.unwrap(cTOs, serverIn); | |
log("server unwrap: ", serverResult); | |
runDelegatedTasks(serverResult, serverEngine); | |
cTOs.compact(); | |
sTOc.compact(); | |
/* | |
* After we've transfered all application data between the client | |
* and server, we close the clientEngine's outbound stream. | |
* This generates a close_notify handshake message, which the | |
* server engine receives and responds by closing itself. | |
* | |
* In normal operation, each SSLEngine should call | |
* closeOutbound(). To protect against truncation attacks, | |
* SSLEngine.closeInbound() should be called whenever it has | |
* determined that no more input data will ever be | |
* available (say a closed input stream). | |
*/ | |
log("Client Out limit " + clientOut.limit() + " Server In Limit " + serverIn.position()); | |
log("Server Out limit " + serverOut.limit() + " Server In Limit " + clientIn.position()); | |
if (!dataDone && (clientOut.limit() == serverIn.position()) && | |
(serverOut.limit() == clientIn.position())) { | |
/* | |
* A sanity check to ensure we got what was sent. | |
*/ | |
checkTransfer(serverOut, clientIn); | |
checkTransfer(clientOut, serverIn); | |
log("\tClosing clientEngine's *OUTBOUND*..."); | |
clientEngine.closeOutbound(); | |
// serverEngine.closeOutbound(); | |
dataDone = true; | |
} | |
} | |
long took = System.currentTimeMillis() - start; | |
System.out.println("Took " + took); | |
System.out.println(dataTransferSize * 1024d / 1024 / 1000 / took + " MB/s"); | |
} | |
/* | |
* Using the SSLContext created during object creation, | |
* create/configure the SSLEngines we'll use for this demo. | |
*/ | |
private void createSSLEngines() throws Exception { | |
/* | |
* Configure the serverEngine to act as a server in the SSL/TLS | |
* handshake. Also, require SSL client authentication. | |
*/ | |
serverEngine = sslc.createSSLEngine(); | |
serverEngine.setUseClientMode(false); | |
serverEngine.setNeedClientAuth(true); | |
/* | |
* Similar to above, but using client mode instead. | |
*/ | |
clientEngine = sslc.createSSLEngine("client", 80); | |
clientEngine.setUseClientMode(true); | |
} | |
/* | |
* Create and size the buffers appropriately. | |
*/ | |
private void createBuffers() { | |
SSLSession session = clientEngine.getSession(); | |
clientIn = ByteBuffer.allocate(dataTransferSize + 50); | |
serverIn = ByteBuffer.allocate(dataTransferSize + 50); | |
cTOs = ByteBuffer.allocateDirect(netBufferSize); | |
sTOc = ByteBuffer.allocateDirect(netBufferSize); | |
clientOut = ByteBuffer.wrap("I'd like some data please".getBytes()); | |
serverOut = ByteBuffer.wrap(new byte[dataTransferSize]); | |
} | |
/* | |
* If the result indicates that we have outstanding tasks to do, | |
* go ahead and run them in this thread. | |
*/ | |
private static void runDelegatedTasks(SSLEngineResult result, | |
SSLEngine engine) throws Exception { | |
if (result.getHandshakeStatus() == HandshakeStatus.NEED_TASK) { | |
Runnable runnable; | |
while ((runnable = engine.getDelegatedTask()) != null) { | |
log("\trunning delegated task..."); | |
runnable.run(); | |
} | |
HandshakeStatus hsStatus = engine.getHandshakeStatus(); | |
if (hsStatus == HandshakeStatus.NEED_TASK) { | |
throw new Exception( | |
"handshake shouldn't need additional tasks"); | |
} | |
log("\tnew HandshakeStatus: " + hsStatus); | |
} | |
if (result.getHandshakeStatus() == HandshakeStatus.NOT_HANDSHAKING) { | |
if (result.getStatus() == Status.BUFFER_OVERFLOW) { | |
} | |
} | |
} | |
private static boolean isEngineClosed(SSLEngine engine) { | |
return (engine.isOutboundDone() && engine.isInboundDone()); | |
} | |
/* | |
* Simple check to make sure everything came across as expected. | |
*/ | |
private static void checkTransfer(ByteBuffer a, ByteBuffer b) | |
throws Exception { | |
a.flip(); | |
b.flip(); | |
if (!a.equals(b)) { | |
throw new Exception("Data didn't transfer cleanly"); | |
} else { | |
log("\tData transferred cleanly"); | |
} | |
a.position(a.limit()); | |
b.position(b.limit()); | |
a.limit(a.capacity()); | |
b.limit(b.capacity()); | |
} | |
/* | |
* Logging code | |
*/ | |
private static boolean resultOnce = true; | |
private static void log(String str, SSLEngineResult result) { | |
if (!logging) { | |
return; | |
} | |
if (resultOnce) { | |
resultOnce = false; | |
System.out.println("The format of the SSLEngineResult is: \n" + | |
"\t\"getStatus() / getHandshakeStatus()\" +\n" + | |
"\t\"bytesConsumed() / bytesProduced()\"\n"); | |
} | |
HandshakeStatus hsStatus = result.getHandshakeStatus(); | |
log(str + | |
result.getStatus() + "/" + hsStatus + ", " + | |
result.bytesConsumed() + "/" + result.bytesProduced() + | |
" bytes"); | |
if (hsStatus == HandshakeStatus.FINISHED) { | |
log("\t...ready for application data"); | |
} | |
} | |
private static void log(String str) { | |
if (logging) { | |
System.out.println(str); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment