Пример #1
0
  /*
   * Begin the shutdown process.
   * <P>
   * Close out the SSLEngine if not already done so, then
   * wrap our outgoing close_notify message and try to send it on.
   * <P>
   * Return true when we're done passing the shutdown messsages.
   */
  boolean shutdown() throws IOException {

    if (!shutdown) {
      sslEngine.closeOutbound();
      shutdown = true;
    }

    if (outNetBB.hasRemaining() && tryFlush(outNetBB)) {
      return false;
    }

    /*
     * By RFC 2616, we can "fire and forget" our close_notify
     * message, so that's what we'll do here.
     */
    outNetBB.clear();
    SSLEngineResult result = sslEngine.wrap(hsBB, outNetBB);
    if (result.getStatus() != Status.CLOSED) {
      throw new SSLException("Improper close state");
    }
    outNetBB.flip();

    /*
     * We won't wait for a select here, but if this doesn't work,
     * we'll cycle back through on the next select.
     */
    if (outNetBB.hasRemaining()) {
      tryFlush(outNetBB);
    }

    return (!outNetBB.hasRemaining() && (result.getHandshakeStatus() != HandshakeStatus.NEED_WRAP));
  }
Пример #2
0
  /*
   * 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);
  }
Пример #3
0
  /*
   * Do all the outstanding handshake tasks in the current Thread.
   */
  private SSLEngineResult.HandshakeStatus doTasks() {

    Runnable runnable;

    /*
     * We could run this in a separate thread, but
     * do in the current for now.
     */
    while ((runnable = sslEngine.getDelegatedTask()) != null) {
      runnable.run();
    }
    return sslEngine.getHandshakeStatus();
  }
Пример #4
0
  /*
   * 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);
    }
  }
Пример #5
0
  /*
   * Using the SSLContext created during object creation,
   * create/configure the SSLEngines we'll use for this test.
   */
  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);
  }
Пример #6
0
  /*
   * Create and size the buffers appropriately.
   */
  private void createBuffers() {

    /*
     * We'll assume the buffer sizes are the same
     * between client and server.
     */
    SSLSession session = clientEngine.getSession();
    int appBufferMax = session.getApplicationBufferSize();
    int netBufferMax = session.getPacketBufferSize();

    /*
     * We'll make the input buffers a bit bigger than the max needed
     * size, so that unwrap()s following a successful data transfer
     * won't generate BUFFER_OVERFLOWS.
     *
     * We'll use a mix of direct and indirect ByteBuffers for
     * tutorial purposes only.  In reality, only use direct
     * ByteBuffers when they give a clear performance enhancement.
     */
    clientIn = ByteBuffer.allocate(appBufferMax + 50);
    serverIn = ByteBuffer.allocate(appBufferMax + 50);

    cTOs = ByteBuffer.allocateDirect(netBufferMax);
    sTOc = ByteBuffer.allocateDirect(netBufferMax);

    clientOut = ByteBuffer.wrap("Hi Server, I'm Client".getBytes());
    serverOut = ByteBuffer.wrap("Hello Client, I'm Server".getBytes());
  }
Пример #7
0
  private void checkResult(
      SSLEngine engine,
      SSLEngineResult result,
      HandshakeStatus rqdHsStatus,
      boolean consumed,
      boolean produced)
      throws Exception {

    HandshakeStatus hsStatus = result.getHandshakeStatus();

    if (hsStatus == HandshakeStatus.NEED_TASK) {
      Runnable runnable;
      while ((runnable = engine.getDelegatedTask()) != null) {
        runnable.run();
      }
      hsStatus = engine.getHandshakeStatus();
    }

    if (hsStatus != rqdHsStatus) {
      throw new Exception("Required " + rqdHsStatus + ", got " + hsStatus);
    }

    int bc = result.bytesConsumed();
    int bp = result.bytesProduced();

    if (consumed) {
      if (bc <= 0) {
        throw new Exception("Should have consumed bytes");
      }
    } else {
      if (bc > 0) {
        throw new Exception("Should not have consumed bytes");
      }
    }

    if (produced) {
      if (bp <= 0) {
        throw new Exception("Should have produced bytes");
      }
    } else {
      if (bp > 0) {
        throw new Exception("Should not have produced bytes");
      }
    }
  }
Пример #8
0
  /*
   * 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);
  }
Пример #9
0
  /*
   * Try to flush out any existing outbound data, then try to wrap
   * anything new contained in the src buffer.
   * <P>
   * Return the number of bytes actually consumed from the buffer,
   * but the data may actually be still sitting in the output buffer,
   * waiting to be flushed.
   */
  private int doWrite(ByteBuffer src) throws IOException {
    int retValue = 0;

    if (outNetBB.hasRemaining() && !tryFlush(outNetBB)) {
      return retValue;
    }

    /*
     * The data buffer is empty, we can reuse the entire buffer.
     */
    outNetBB.clear();

    SSLEngineResult result = sslEngine.wrap(src, outNetBB);
    retValue = result.bytesConsumed();

    outNetBB.flip();

    switch (result.getStatus()) {
      case OK:
        if (result.getHandshakeStatus() == HandshakeStatus.NEED_TASK) {
          doTasks();
        }
        break;

      default:
        throw new IOException("sslEngine error during data write: " + result.getStatus());
    }

    /*
     * Try to flush the data, regardless of whether or not
     * it's been selected.  Odds of a write buffer being full
     * is less than a read buffer being empty.
     */
    if (outNetBB.hasRemaining()) {
      tryFlush(outNetBB);
    }

    return retValue;
  }
Пример #10
0
 private static boolean isEngineClosed(SSLEngine engine) {
   return (engine.isOutboundDone() && engine.isInboundDone());
 }
Пример #11
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();
  }
Пример #12
0
  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();
  }
Пример #13
0
  /*
   * 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;
  }