Пример #1
0
  /*
   * Main entry point for this test.
   */
  public static void main(String args[]) throws Exception {
    if (debug) {
      System.setProperty("javax.net.debug", "all");
    }

    if (loadException != null) {
      throw loadException;
    }

    // Prime the session cache with a good session
    // Second connection should be a simple session resumption.
    if ((new RehandshakeFinished().runTest()) != new RehandshakeFinished().runRehandshake()) {
      throw new Exception("Sessions not equivalent");
    }

    System.out.println("Test Passed.");
  }
Пример #2
0
public class RehandshakeFinished {

  /*
   * Enables logging of the SSLEngine operations.
   */
  private static boolean logging = true;

  /*
   * 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 static 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 pathToStores = "../../../../../../../etc";
  private static String keyStoreFile = "keystore";
  private static String trustStoreFile = "truststore";
  private static String passwd = "passphrase";

  private static String keyFilename =
      System.getProperty("test.src", "./") + "/" + pathToStores + "/" + keyStoreFile;
  private static String trustFilename =
      System.getProperty("test.src", "./") + "/" + pathToStores + "/" + trustStoreFile;

  private static Exception loadException = null;

  static {
    try {
      KeyStore ks = KeyStore.getInstance("JKS");
      KeyStore ts = KeyStore.getInstance("JKS");

      char[] passphrase = "passphrase".toCharArray();

      ks.load(new FileInputStream(keyFilename), passphrase);
      ts.load(new FileInputStream(trustFilename), 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;
    } catch (Exception e) {
      loadException = e;
    }
  }

  /*
   * Main entry point for this test.
   */
  public static void main(String args[]) throws Exception {
    if (debug) {
      System.setProperty("javax.net.debug", "all");
    }

    if (loadException != null) {
      throw loadException;
    }

    // Prime the session cache with a good session
    // Second connection should be a simple session resumption.
    if ((new RehandshakeFinished().runTest()) != new RehandshakeFinished().runRehandshake()) {
      throw new Exception("Sessions not equivalent");
    }

    System.out.println("Test Passed.");
  }

  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");
      }
    }
  }

  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();
  }

  /*
   * 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();
  }

  /*
   * 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);
  }

  /*
   * 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());
  }

  /*
   * 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);
    }
  }

  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);
    }
  }
}