Пример #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
 public SSLSession getSSLSession() {
   SSLEngine e = connection.getSSLEngine();
   if (e == null) {
     return null;
   }
   return e.getSession();
 }
Пример #3
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);
  }
Пример #4
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();
  }
Пример #5
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);
  }
Пример #6
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;
  }
Пример #7
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;
  }