private SSLEngineResult unwrap( SSLEngine engine, ByteBuf in, int readerIndex, int len, ByteBuf out) throws SSLException { int nioBufferCount = in.nioBufferCount(); int writerIndex = out.writerIndex(); final SSLEngineResult result; if (engine instanceof OpenSslEngine && nioBufferCount > 1) { /** * If {@link OpenSslEngine} is in use, we can use a special {@link * OpenSslEngine#unwrap(ByteBuffer[], ByteBuffer[])} method that accepts multiple {@link * ByteBuffer}s without additional memory copies. */ OpenSslEngine opensslEngine = (OpenSslEngine) engine; try { singleBuffer[0] = toByteBuffer(out, writerIndex, out.writableBytes()); result = opensslEngine.unwrap(in.nioBuffers(readerIndex, len), singleBuffer); out.writerIndex(writerIndex + result.bytesProduced()); } finally { singleBuffer[0] = null; } } else { result = engine.unwrap( toByteBuffer(in, readerIndex, len), toByteBuffer(out, writerIndex, out.writableBytes())); } out.writerIndex(writerIndex + result.bytesProduced()); return result; }
protected boolean handleSSLHandshake2() throws IOException { // We need to make sure the handshake process finished as a whole // boolean proceed = false; SSLEngineResult engineResult = null; isHanshakeDone = false; while (true) { switch (engine.getHandshakeStatus()) { case NOT_HANDSHAKING: case FINISHED: isHanshakeDone = true; return true; case NEED_TASK: Executor exec = Executors.newSingleThreadExecutor(); Runnable task; while ((task = engine.getDelegatedTask()) != null) { exec.execute(task); } continue; case NEED_WRAP: // We need to call a wrap on the engine appSendBuffer.flip(); engineResult = engine.wrap(appSendBuffer, netSendBuffer); appSendBuffer.compact(); if (engineResult.getStatus() == Status.BUFFER_OVERFLOW || engineResult.getStatus() == Status .OK) { // The enigne sys we need to flush the current buffer into the network // So just set the selector to the write mode channel.register(selector, SelectionKey.OP_WRITE); selector.wakeup(); // System.out.println("Handshake wants to do a write "); return false; } else if (engineResult.getStatus() == Status.CLOSED) { throw new IOException("Connection closed"); } else { continue; } case NEED_UNWRAP: // We need to call unwarap method of the engine netRecvBuffer.flip(); engineResult = engine.unwrap(netRecvBuffer, appRecvBuffer); netRecvBuffer.compact(); // System.out.println(engine.isInboundDone()); if (engineResult.getStatus() == Status.BUFFER_UNDERFLOW) { if (!engine.isInboundDone()) { channel.register(selector, SelectionKey.OP_READ); selector.wakeup(); // System.out.println("Handshake wants to do a read "); return false; } else continue; } else if (engineResult.getStatus() == Status.CLOSED) { throw new IOException("Connection closed"); } else { continue; } } } }
private int read() throws IOException { // System.out.println("Doing a read "); int count = 0; while ((count = channel.read(netRecvBuffer)) > 0) ; if (count == -1) throw new IOException("Connection closed"); if (!isHanshakeDone) { if (!handleSSLHandshake2()) return 0; else { if (appSendBuffer.hasRemaining()) { channel.register(selector, SelectionKey.OP_WRITE); selector.wakeup(); return 0; } } } int retval = 0; boolean loop = true; while (loop) { netRecvBuffer.flip(); SSLEngineResult engineResult = engine.unwrap(netRecvBuffer, appRecvBuffer); netRecvBuffer.compact(); switch (engineResult.getStatus()) { case BUFFER_UNDERFLOW: // There is not enough data read from the network buffer to do the // handshaking // Setting the selector to be read again and pass 0 to the caller saying there is nothing // to read // System.out.println("Read buffer underflow"); channel.register(selector, SelectionKey.OP_READ); selector.wakeup(); loop = false; break; case BUFFER_OVERFLOW: // The appbuffer is full the caller really needs to read the current // buffer. // Please not this not a problem in the our current implementation as we make sure we are // writing the data to the stream // System.out.println("Read buffer overflow"); retval = appRecvBuffer.position(); loop = false; break; case CLOSED: throw new IOException("Connection closed"); case OK: retval = appRecvBuffer.position(); break; default: break; } if (!handleSSLHandshake2()) { loop = false; retval = 0; } } return retval; }
/* * 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); }
/** * Method description * * @param net * @param app * @return * @throws SSLException */ public ByteBuffer unwrap(ByteBuffer net, ByteBuffer app) throws SSLException { ByteBuffer out = app; out.order(app.order()); tlsEngineResult = tlsEngine.unwrap(net, out); if (log.isLoggable(Level.FINEST)) { log.log( Level.FINEST, "{0}, unwrap() tlsEngineRsult.getStatus() = {1}, " + "tlsEngineRsult.getHandshakeStatus() = {2}", new Object[] { debugId, tlsEngineResult.getStatus(), tlsEngineResult.getHandshakeStatus() }); } if (tlsEngineResult.getHandshakeStatus() == SSLEngineResult.HandshakeStatus.FINISHED) { if (eventHandler != null) { eventHandler.handshakeCompleted(this); } } if (tlsEngineResult.getStatus() == Status.BUFFER_OVERFLOW) { out = resizeApplicationBuffer(net, out); tlsEngineResult = tlsEngine.unwrap(net, out); } if (tlsEngineResult.getHandshakeStatus() == HandshakeStatus.NEED_TASK) { doTasks(); if (log.isLoggable(Level.FINEST)) { log.log( Level.FINEST, "unwrap() doTasks(), handshake: {0}, {1}", new Object[] {tlsEngine.getHandshakeStatus(), debugId}); } } return out; }
@Test public void testAbruptCloseDuringHandshake() throws Exception { InetSocketAddress address = startServer(version, null); SslContextFactory sslContextFactory = newSslContextFactory(); sslContextFactory.start(); SSLEngine sslEngine = sslContextFactory.newSSLEngine(address); sslEngine.setUseClientMode(true); NextProtoNego.put( sslEngine, new NextProtoNego.ClientProvider() { @Override public boolean supports() { return true; } @Override public void unsupported() {} @Override public String selectProtocol(List<String> protocols) { return null; } }); sslEngine.beginHandshake(); ByteBuffer encrypted = ByteBuffer.allocate(sslEngine.getSession().getPacketBufferSize()); sslEngine.wrap(BufferUtil.EMPTY_BUFFER, encrypted); encrypted.flip(); try (SocketChannel channel = SocketChannel.open(address)) { // Send ClientHello, immediately followed by FIN (no TLS Close Alert) channel.write(encrypted); channel.shutdownOutput(); // Read ServerHello from server encrypted.clear(); int read = channel.read(encrypted); encrypted.flip(); Assert.assertTrue(read > 0); ByteBuffer decrypted = ByteBuffer.allocate(sslEngine.getSession().getApplicationBufferSize()); sslEngine.unwrap(encrypted, decrypted); // Now if we read more, we should either read the TLS Close Alert, or directly -1 encrypted.clear(); read = channel.read(encrypted); // Since we have close the connection abruptly, the server also does so Assert.assertTrue(read < 0); } }
private static SSLEngineResult unwrap(SSLEngine engine, ByteBuf in, ByteBuf out) throws SSLException { ByteBuffer in0 = in.nioBuffer(); for (; ; ) { ByteBuffer out0 = out.nioBuffer(out.writerIndex(), out.writableBytes()); SSLEngineResult result = engine.unwrap(in0, out0); in.skipBytes(result.bytesConsumed()); out.writerIndex(out.writerIndex() + result.bytesProduced()); switch (result.getStatus()) { case BUFFER_OVERFLOW: out.ensureWritableBytes(engine.getSession().getApplicationBufferSize()); break; default: return result; } } }
/** * Unwraps data with the specified engine. * * @param engine - SSLEngine that unwraps data. * @param unwrapper - Set unwrapper id, e.g. "server" of "client". Used for logging only. * @param net - Buffer with data to unwrap. * @param wantedStatus - Specifies expected result status of wrapping. * @param result - Array which first element will be used to output wrap result object. * @return - Buffer with unwrapped data. * @throws SSLException - thrown on engine errors. */ public static ByteBuffer doUnWrap( SSLEngine engine, String unwrapper, ByteBuffer net, SSLEngineResult.Status wantedStatus, SSLEngineResult[] result) throws SSLException { ByteBuffer app = ByteBuffer.allocate(engine.getSession().getApplicationBufferSize()); int length = net.remaining(); System.out.println(unwrapper + " unwrapping " + length + " bytes..."); SSLEngineResult r = engine.unwrap(net, app); app.flip(); System.out.println(unwrapper + " handshake status is " + engine.getHandshakeStatus()); checkResult(r, wantedStatus); if (result != null && result.length > 0) { result[0] = r; } return app; }
private boolean unwrap() { SSLEngineResult unwrapResult; try { unwrapSrc.flip(); unwrapResult = engine.unwrap(unwrapSrc, unwrapDst); unwrapSrc.compact(); } catch (SSLException exc) { this.onHandshakeFailure(exc); return false; } switch (unwrapResult.getStatus()) { case OK: if (unwrapDst.position() > 0) { unwrapDst.flip(); this.onInboundData(unwrapDst); unwrapDst.compact(); } break; case CLOSED: this.onClosed(); return false; case BUFFER_OVERFLOW: throw new IllegalStateException("failed to unwrap"); case BUFFER_UNDERFLOW: return false; } switch (unwrapResult.getHandshakeStatus()) { case FINISHED: this.onHandshakeSuccess(); return false; } return true; }
/** * Attempts to decode SSL/TLS network data into a plaintext application data buffer. * * @param src a ByteBuffer containing inbound network data. * @param dst a ByteBuffer to hold inbound application data. * @return the number of bytes produced * @throws Exception */ private int unwrap(ByteBuffer src, ByteBuffer dst) throws Exception { SSLEngineResult result; int read = 0; do { // prepare the input buffer src.flip(); // unwrap the data result = sslEngine.unwrap(src, dst); // compact the buffer src.compact(); if (result.getStatus() == Status.OK || result.getStatus() == Status.BUFFER_UNDERFLOW) { // we did receive some data, add it to our total read += result.bytesProduced(); handshakeStatus = result.getHandshakeStatus(); // perform any tasks if needed tryTasks(); // if we need more network data, then bail out for now. if (result.getStatus() == Status.BUFFER_UNDERFLOW) { break; } } else if (result.getStatus() == Status.BUFFER_OVERFLOW && read > 0) { // buffer overflow can happen, if we have read data, then // empty out the destination buffer before we do another read break; } else if (result.getStatus() == Status.CLOSED) { return -1; } else { // here we should trap BUFFER_OVERFLOW and call expand on the // buffer for now, throw an exception, as we initialized the // buffers in the constructor throw new IOException(MESSAGES.errorUnwrappingData(result.getStatus().toString())); } // continue to unwrapping as long as the input buffer has stuff } while (src.position() != 0); return read; }
private ByteBuffer unwrap(ByteBuffer b) throws SSLException { in.clear(); while (b.hasRemaining()) { sslEngineResult = sslEngine.unwrap(b, in); if (sslEngineResult.getHandshakeStatus() == SSLEngineResult.HandshakeStatus.NEED_TASK) { if (DEBUG) { log("Handshake NEED TASK"); } Runnable task; while ((task = sslEngine.getDelegatedTask()) != null) { if (DEBUG) { log("Running task: " + task); } task.run(); } } else if (sslEngineResult.getHandshakeStatus() == SSLEngineResult.HandshakeStatus.FINISHED || sslEngineResult.getStatus() == SSLEngineResult.Status.BUFFER_UNDERFLOW) { return in; } } return in; }
private static SSLEngineResult unwrap(SSLEngine engine, ByteBuffer in, ByteBuf out) throws SSLException { int overflows = 0; for (; ; ) { ByteBuffer out0 = out.nioBuffer(out.writerIndex(), out.writableBytes()); SSLEngineResult result = engine.unwrap(in, out0); out.writerIndex(out.writerIndex() + result.bytesProduced()); switch (result.getStatus()) { case BUFFER_OVERFLOW: int max = engine.getSession().getApplicationBufferSize(); switch (overflows++) { case 0: out.ensureWritable(Math.min(max, in.remaining())); break; default: out.ensureWritable(max); } break; default: return result; } } }
private boolean unwrap() throws IOException { while (true) { SSLEngineResult result = engine.unwrap(inToken, data); switch (result.getStatus()) { case BUFFER_UNDERFLOW: return false; case BUFFER_OVERFLOW: ByteBuffer buffer = ByteBuffer.allocate( outToken.capacity() + engine.getSession().getApplicationBufferSize()); data.flip(); buffer.put(data); data = buffer; break; case OK: if (result.getHandshakeStatus() == SSLEngineResult.HandshakeStatus.FINISHED) { subject = createSubject(); } return true; case CLOSED: throw new EOFException(); } } }
@Test public void checkSslEngineBehaviour() throws Exception { SSLEngine server = __sslCtxFactory.newSSLEngine(); SSLEngine client = __sslCtxFactory.newSSLEngine(); ByteBuffer netC2S = ByteBuffer.allocate(server.getSession().getPacketBufferSize()); ByteBuffer netS2C = ByteBuffer.allocate(server.getSession().getPacketBufferSize()); ByteBuffer serverIn = ByteBuffer.allocate(server.getSession().getApplicationBufferSize()); ByteBuffer serverOut = ByteBuffer.allocate(server.getSession().getApplicationBufferSize()); ByteBuffer clientIn = ByteBuffer.allocate(client.getSession().getApplicationBufferSize()); SSLEngineResult result; // start the client client.setUseClientMode(true); client.beginHandshake(); Assert.assertEquals(HandshakeStatus.NEED_WRAP, client.getHandshakeStatus()); // what if we try an unwrap? netS2C.flip(); result = client.unwrap(netS2C, clientIn); // unwrap is a noop assertEquals(SSLEngineResult.Status.OK, result.getStatus()); assertEquals(0, result.bytesConsumed()); assertEquals(0, result.bytesProduced()); assertEquals(HandshakeStatus.NEED_WRAP, result.getHandshakeStatus()); netS2C.clear(); // do the needed WRAP of empty buffer result = client.wrap(BufferUtil.EMPTY_BUFFER, netC2S); // unwrap is a noop assertEquals(SSLEngineResult.Status.OK, result.getStatus()); assertEquals(0, result.bytesConsumed()); assertThat(result.bytesProduced(), greaterThan(0)); assertEquals(HandshakeStatus.NEED_UNWRAP, result.getHandshakeStatus()); netC2S.flip(); assertEquals(netC2S.remaining(), result.bytesProduced()); // start the server server.setUseClientMode(false); server.beginHandshake(); Assert.assertEquals(HandshakeStatus.NEED_UNWRAP, server.getHandshakeStatus()); // what if we try a needless wrap? serverOut.put(BufferUtil.toBuffer("Hello World")); serverOut.flip(); result = server.wrap(serverOut, netS2C); // wrap is a noop assertEquals(SSLEngineResult.Status.OK, result.getStatus()); assertEquals(0, result.bytesConsumed()); assertEquals(0, result.bytesProduced()); assertEquals(HandshakeStatus.NEED_UNWRAP, result.getHandshakeStatus()); // Do the needed unwrap, to an empty buffer result = server.unwrap(netC2S, BufferUtil.EMPTY_BUFFER); assertEquals(SSLEngineResult.Status.BUFFER_OVERFLOW, result.getStatus()); assertEquals(0, result.bytesConsumed()); assertEquals(0, result.bytesProduced()); assertEquals(HandshakeStatus.NEED_UNWRAP, result.getHandshakeStatus()); // Do the needed unwrap, to a full buffer serverIn.position(serverIn.limit()); result = server.unwrap(netC2S, serverIn); assertEquals(SSLEngineResult.Status.BUFFER_OVERFLOW, result.getStatus()); assertEquals(0, result.bytesConsumed()); assertEquals(0, result.bytesProduced()); assertEquals(HandshakeStatus.NEED_UNWRAP, result.getHandshakeStatus()); // Do the needed unwrap, to an empty buffer serverIn.clear(); result = server.unwrap(netC2S, serverIn); assertEquals(SSLEngineResult.Status.OK, result.getStatus()); assertThat(result.bytesConsumed(), greaterThan(0)); assertEquals(0, result.bytesProduced()); assertEquals(HandshakeStatus.NEED_TASK, result.getHandshakeStatus()); server.getDelegatedTask().run(); assertEquals(HandshakeStatus.NEED_WRAP, server.getHandshakeStatus()); }
/** * Execute a handshake with the client socket channel * * @throws Exception */ private void doHandshake() throws Exception { SSLSession session = getSSLSession(); int packetBufferSize = Math.max(session.getPacketBufferSize(), MIN_BUFFER_SIZE); // Create byte buffers to use for holding application data initBuffers(packetBufferSize); ByteBuffer clientNetData = ByteBuffer.allocateDirect(packetBufferSize); ByteBuffer clientAppData = ByteBuffer.allocateDirect(packetBufferSize); // Begin handshake sslEngine.beginHandshake(); handshakeStatus = sslEngine.getHandshakeStatus(); int i = 1; boolean read = true; // Process handshaking message while (!handshakeComplete) { switch (handshakeStatus) { case NEED_UNWRAP: int nBytes = 0; if (read) { clientAppData.clear(); nBytes = this.channel.read(this.netInBuffer).get(); } if (nBytes < 0) { throw new IOException(MESSAGES.errorUnwrappingHandshake()); } else { boolean cont = false; // Loop while we can perform pure SSLEngine data do { // Prepare the buffer with the incoming data this.netInBuffer.flip(); // Call unwrap SSLEngineResult res = sslEngine.unwrap(this.netInBuffer, clientAppData); // Compact the buffer, this is an optional method, // wonder what would happen if we didn't this.netInBuffer.compact(); // Read in the status handshakeStatus = res.getHandshakeStatus(); if (res.getStatus() == SSLEngineResult.Status.OK) { // Execute tasks if we need to tryTasks(); read = true; } else if (res.getStatus() == Status.BUFFER_UNDERFLOW) { read = true; } else if (res.getStatus() == Status.BUFFER_OVERFLOW) { ByteBuffer tmp = ByteBuffer.allocateDirect(packetBufferSize * (++i)); if (clientAppData.position() > 0) { clientAppData.flip(); } tmp.put(clientAppData); clientAppData = tmp; read = false; } // Perform another unwrap? cont = res.getStatus() == SSLEngineResult.Status.OK && handshakeStatus == HandshakeStatus.NEED_UNWRAP; } while (cont); } break; case NEED_WRAP: clientNetData.compact(); this.netOutBuffer.clear(); SSLEngineResult res = sslEngine.wrap(clientNetData, this.netOutBuffer); handshakeStatus = res.getHandshakeStatus(); this.netOutBuffer.flip(); if (res.getStatus() == Status.OK) { // Execute tasks if we need to tryTasks(); // Send the handshaking data to client while (this.netOutBuffer.hasRemaining()) { if (this.channel.write(this.netOutBuffer).get() < 0) { // Handle closed channel throw new IOException(MESSAGES.errorWrappingHandshake()); } } } else { // Wrap should always work with our buffers throw new IOException( MESSAGES.errorWrappingHandshakeStatus(res.getStatus().toString())); } break; case NEED_TASK: handshakeStatus = tasks(); break; case NOT_HANDSHAKING: throw new SSLHandshakeException(MESSAGES.notHandshaking()); case FINISHED: handshakeComplete = true; break; } } this.handshakeComplete = (handshakeStatus == HandshakeStatus.FINISHED); }
private ChannelBuffer unwrap( ChannelHandlerContext ctx, Channel channel, ChannelBuffer buffer, int offset, int length) throws SSLException { ByteBuffer inNetBuf = buffer.toByteBuffer(offset, length); ByteBuffer outAppBuf = bufferPool.acquire(); try { boolean needsWrap = false; loop: for (; ; ) { SSLEngineResult result; synchronized (handshakeLock) { if (!handshaken && !handshaking && !engine.getUseClientMode() && !engine.isInboundDone() && !engine.isOutboundDone()) { handshake(); } try { result = engine.unwrap(inNetBuf, outAppBuf); } catch (SSLException e) { throw e; } final HandshakeStatus handshakeStatus = result.getHandshakeStatus(); handleRenegotiation(handshakeStatus); switch (handshakeStatus) { case NEED_UNWRAP: if (inNetBuf.hasRemaining() && !engine.isInboundDone()) { break; } else { break loop; } case NEED_WRAP: wrapNonAppData(ctx, channel); break; case NEED_TASK: runDelegatedTasks(); break; case FINISHED: setHandshakeSuccess(channel); needsWrap = true; break loop; case NOT_HANDSHAKING: needsWrap = true; break loop; default: throw new IllegalStateException("Unknown handshake status: " + handshakeStatus); } } } if (needsWrap) { // wrap() acquires pendingUnencryptedWrites first and then // handshakeLock. If handshakeLock is already hold by the // current thread, calling wrap() will lead to a dead lock // i.e. pendingUnencryptedWrites -> handshakeLock vs. // handshakeLock -> pendingUnencryptedLock -> handshakeLock // // There is also a same issue between pendingEncryptedWrites // and pendingUnencryptedWrites. if (!Thread.holdsLock(handshakeLock) && !pendingEncryptedWritesLock.isHeldByCurrentThread()) { wrap(ctx, channel); } } outAppBuf.flip(); if (outAppBuf.hasRemaining()) { ChannelBuffer frame = ChannelBuffers.buffer(outAppBuf.remaining()); frame.writeBytes(outAppBuf.array(), 0, frame.capacity()); return frame; } else { return null; } } catch (SSLException e) { setHandshakeFailure(channel, e); throw e; } finally { bufferPool.release(outAppBuf); } }
/* * 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(); }
private SSLSession runRehandshake() throws Exception { log("\n\n=============================================="); log("Staring actual test."); createSSLEngines(); createBuffers(); SSLEngineResult result; log("Client's ClientHello"); checkResult( clientEngine, clientEngine.wrap(clientOut, cTOs), HandshakeStatus.NEED_UNWRAP, false, true); cTOs.flip(); checkResult( serverEngine, serverEngine.unwrap(cTOs, serverIn), HandshakeStatus.NEED_WRAP, true, false); cTOs.compact(); log("Server's ServerHello/ServerHelloDone"); checkResult( serverEngine, serverEngine.wrap(serverOut, sTOc), HandshakeStatus.NEED_WRAP, false, true); sTOc.flip(); checkResult( clientEngine, clientEngine.unwrap(sTOc, clientIn), HandshakeStatus.NEED_UNWRAP, true, false); sTOc.compact(); log("Server's CCS"); checkResult( serverEngine, serverEngine.wrap(serverOut, sTOc), HandshakeStatus.NEED_WRAP, false, true); sTOc.flip(); checkResult( clientEngine, clientEngine.unwrap(sTOc, clientIn), HandshakeStatus.NEED_UNWRAP, true, false); sTOc.compact(); log("Server's FINISHED"); checkResult( serverEngine, serverEngine.wrap(serverOut, sTOc), HandshakeStatus.NEED_UNWRAP, false, true); sTOc.flip(); checkResult( clientEngine, clientEngine.unwrap(sTOc, clientIn), HandshakeStatus.NEED_WRAP, true, false); sTOc.compact(); log("Client's CCS"); checkResult( clientEngine, clientEngine.wrap(clientOut, cTOs), HandshakeStatus.NEED_WRAP, false, true); cTOs.flip(); checkResult( serverEngine, serverEngine.unwrap(cTOs, serverIn), HandshakeStatus.NEED_UNWRAP, true, false); cTOs.compact(); log("Client's FINISHED should trigger FINISHED messages all around."); checkResult( clientEngine, clientEngine.wrap(clientOut, cTOs), HandshakeStatus.FINISHED, false, true); cTOs.flip(); checkResult( serverEngine, serverEngine.unwrap(cTOs, serverIn), HandshakeStatus.FINISHED, true, false); cTOs.compact(); return clientEngine.getSession(); }
private SSLEngineResult doUnwrap() throws SSLException { ByteBuffer cipherText = _buffers.get(BufferType.IN_CIPHER); ByteBuffer plainText = _buffers.get(BufferType.IN_PLAIN); return _engine.unwrap(cipherText, plainText); }
/* * Perform any handshaking processing. * <P> * If a SelectionKey is passed, register for selectable * operations. * <P> * In the blocking case, our caller will keep calling us until * we finish the handshake. Our reads/writes will block as expected. * <P> * In the non-blocking case, we just received the selection notification * that this channel is ready for whatever the operation is, so give * it a try. * <P> * return: * true when handshake is done. * false while handshake is in progress */ boolean doHandshake(SelectionKey sk) throws IOException { SSLEngineResult result; if (initialHSComplete) { return initialHSComplete; } /* * Flush out the outgoing buffer, if there's anything left in * it. */ if (outNetBB.hasRemaining()) { if (!tryFlush(outNetBB)) { return false; } // See if we need to switch from write to read mode. switch (initialHSStatus) { /* * Is this the last buffer? */ case FINISHED: initialHSComplete = true; // Fall-through to reregister need for a Read. case NEED_UNWRAP: if (sk != null) { sk.interestOps(SelectionKey.OP_READ); } break; } return initialHSComplete; } switch (initialHSStatus) { case NEED_UNWRAP: if (sc.read(inNetBB) == -1) { sslEngine.closeInbound(); return initialHSComplete; } needIO: while (initialHSStatus == HandshakeStatus.NEED_UNWRAP) { resizeRequestBB(); // expected room for unwrap inNetBB.flip(); result = sslEngine.unwrap(inNetBB, requestBB); inNetBB.compact(); initialHSStatus = result.getHandshakeStatus(); switch (result.getStatus()) { case OK: switch (initialHSStatus) { case NOT_HANDSHAKING: throw new IOException("Not handshaking during initial handshake"); case NEED_TASK: initialHSStatus = doTasks(); break; case FINISHED: initialHSComplete = true; break needIO; } break; case BUFFER_UNDERFLOW: // Resize buffer if needed. netBBSize = sslEngine.getSession().getPacketBufferSize(); if (netBBSize > inNetBB.capacity()) { resizeResponseBB(); } /* * Need to go reread the Channel for more data. */ if (sk != null) { sk.interestOps(SelectionKey.OP_READ); } break needIO; case BUFFER_OVERFLOW: // Reset the application buffer size. appBBSize = sslEngine.getSession().getApplicationBufferSize(); break; default: // CLOSED: throw new IOException("Received" + result.getStatus() + "during initial handshaking"); } } // "needIO" block. /* * Just transitioned from read to write. */ if (initialHSStatus != HandshakeStatus.NEED_WRAP) { break; } // Fall through and fill the write buffers. case NEED_WRAP: /* * The flush above guarantees the out buffer to be empty */ outNetBB.clear(); result = sslEngine.wrap(hsBB, outNetBB); outNetBB.flip(); initialHSStatus = result.getHandshakeStatus(); switch (result.getStatus()) { case OK: if (initialHSStatus == HandshakeStatus.NEED_TASK) { initialHSStatus = doTasks(); } if (sk != null) { sk.interestOps(SelectionKey.OP_WRITE); } break; default: // BUFFER_OVERFLOW/BUFFER_UNDERFLOW/CLOSED: throw new IOException("Received" + result.getStatus() + "during initial handshaking"); } break; default: // NOT_HANDSHAKING/NEED_TASK/FINISHED throw new RuntimeException("Invalid Handshaking State" + initialHSStatus); } // switch return initialHSComplete; }
@Test public void testTcpClose() throws Exception { // This test replaces SSLSocket() with a very manual SSL client // so we can close TCP underneath SSL. SocketChannel client = SocketChannel.open(_connector.socket().getLocalSocketAddress()); client.socket().setSoTimeout(500); SocketChannel server = _connector.accept(); server.configureBlocking(false); _manager.accept(server); SSLEngine engine = __sslCtxFactory.newSSLEngine(); engine.setUseClientMode(true); engine.beginHandshake(); ByteBuffer appOut = ByteBuffer.allocate(engine.getSession().getApplicationBufferSize()); ByteBuffer sslOut = ByteBuffer.allocate(engine.getSession().getPacketBufferSize() * 2); ByteBuffer appIn = ByteBuffer.allocate(engine.getSession().getApplicationBufferSize()); ByteBuffer sslIn = ByteBuffer.allocate(engine.getSession().getPacketBufferSize() * 2); boolean debug = false; if (debug) System.err.println(engine.getHandshakeStatus()); int loop = 20; while (engine.getHandshakeStatus() != HandshakeStatus.NOT_HANDSHAKING) { if (--loop == 0) throw new IllegalStateException(); if (engine.getHandshakeStatus() == HandshakeStatus.NEED_WRAP) { if (debug) System.err.printf( "sslOut %d-%d-%d%n", sslOut.position(), sslOut.limit(), sslOut.capacity()); if (debug) System.err.printf( "appOut %d-%d-%d%n", appOut.position(), appOut.limit(), appOut.capacity()); SSLEngineResult result = engine.wrap(appOut, sslOut); if (debug) System.err.println(result); sslOut.flip(); int flushed = client.write(sslOut); if (debug) System.err.println("out=" + flushed); sslOut.clear(); } if (engine.getHandshakeStatus() == HandshakeStatus.NEED_UNWRAP) { if (debug) System.err.printf("sslIn %d-%d-%d%n", sslIn.position(), sslIn.limit(), sslIn.capacity()); if (sslIn.position() == 0) { int filled = client.read(sslIn); if (debug) System.err.println("in=" + filled); } sslIn.flip(); if (debug) System.err.printf("sslIn %d-%d-%d%n", sslIn.position(), sslIn.limit(), sslIn.capacity()); SSLEngineResult result = engine.unwrap(sslIn, appIn); if (debug) System.err.println(result); if (debug) System.err.printf("sslIn %d-%d-%d%n", sslIn.position(), sslIn.limit(), sslIn.capacity()); if (sslIn.hasRemaining()) sslIn.compact(); else sslIn.clear(); if (debug) System.err.printf("sslIn %d-%d-%d%n", sslIn.position(), sslIn.limit(), sslIn.capacity()); } if (engine.getHandshakeStatus() == HandshakeStatus.NEED_TASK) { Runnable task; while ((task = engine.getDelegatedTask()) != null) task.run(); if (debug) System.err.println(engine.getHandshakeStatus()); } } if (debug) System.err.println("\nSay Hello"); // write a message appOut.put("HelloWorld".getBytes(StandardCharsets.UTF_8)); appOut.flip(); SSLEngineResult result = engine.wrap(appOut, sslOut); if (debug) System.err.println(result); sslOut.flip(); int flushed = client.write(sslOut); if (debug) System.err.println("out=" + flushed); sslOut.clear(); appOut.clear(); // read the response int filled = client.read(sslIn); if (debug) System.err.println("in=" + filled); sslIn.flip(); result = engine.unwrap(sslIn, appIn); if (debug) System.err.println(result); if (sslIn.hasRemaining()) sslIn.compact(); else sslIn.clear(); appIn.flip(); String reply = new String(appIn.array(), appIn.arrayOffset(), appIn.remaining()); appIn.clear(); Assert.assertEquals("HelloWorld", reply); if (debug) System.err.println("Shutting down output"); client.socket().shutdownOutput(); filled = client.read(sslIn); if (debug) System.err.println("in=" + filled); if (filled >= 0) { // this is the old behaviour. sslIn.flip(); try { // Since the client closed abruptly, the server is sending a close alert with a failure engine.unwrap(sslIn, appIn); Assert.fail(); } catch (SSLException x) { // Expected } } sslIn.clear(); filled = client.read(sslIn); Assert.assertEquals(-1, filled); Assert.assertFalse(server.isOpen()); }