/* * 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()); }
/* * Read the channel for more information, then unwrap the * (hopefully application) data we get. * <P> * If we run out of data, we'll return to our caller (possibly using * a Selector) to get notification that more is available. * <P> * Each call to this method will perform at most one underlying read(). */ int read() throws IOException { SSLEngineResult result; if (!initialHSComplete) { throw new IllegalStateException(); } int pos = requestBB.position(); if (sc.read(inNetBB) == -1) { sslEngine.closeInbound(); // probably throws exception return -1; } do { resizeRequestBB(); // expected room for unwrap inNetBB.flip(); result = sslEngine.unwrap(inNetBB, requestBB); inNetBB.compact(); /* * Could check here for a renegotation, but we're only * doing a simple read/write, and won't have enough state * transitions to do a complete handshake, so ignore that * possibility. */ switch (result.getStatus()) { case BUFFER_OVERFLOW: // Reset the application buffer size. appBBSize = sslEngine.getSession().getApplicationBufferSize(); break; case BUFFER_UNDERFLOW: // Resize buffer if needed. netBBSize = sslEngine.getSession().getPacketBufferSize(); if (netBBSize > inNetBB.capacity()) { resizeResponseBB(); break; // break, next read will support larger buffer. } case OK: if (result.getHandshakeStatus() == HandshakeStatus.NEED_TASK) { doTasks(); } break; default: throw new IOException("sslEngine error during data read: " + result.getStatus()); } } while ((inNetBB.position() != 0) && result.getStatus() != Status.BUFFER_UNDERFLOW); return (requestBB.position() - pos); }
/* * Constructor for a secure ChannelIO variant. */ protected ChannelIOSecure(SocketChannel sc, boolean blocking, SSLContext sslc) throws IOException { super(sc, blocking); /* * We're a server, so no need to use host/port variant. * * The first call for a server is a NEED_UNWRAP. */ sslEngine = sslc.createSSLEngine(); sslEngine.setUseClientMode(false); initialHSStatus = HandshakeStatus.NEED_UNWRAP; initialHSComplete = false; // Create a buffer using the normal expected packet size we'll // be getting. This may change, depending on the peer's // SSL implementation. netBBSize = sslEngine.getSession().getPacketBufferSize(); inNetBB = ByteBuffer.allocate(netBBSize); outNetBB = ByteBuffer.allocate(netBBSize); outNetBB.position(0); outNetBB.limit(0); }
/* * Run the test. * * 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 SSLSession runTest() throws Exception { 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. */ 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(); dataDone = true; } } return clientEngine.getSession(); }