Esempio n. 1
0
  /*
   * Get the active protocol versions.
   *
   * In TLS 1.1, many weak or vulnerable cipher suites were obsoleted,
   * such as TLS_RSA_EXPORT_WITH_RC4_40_MD5. The implementation MUST NOT
   * negotiate these cipher suites in TLS 1.1 or later mode.
   *
   * For example, if "TLS_RSA_EXPORT_WITH_RC4_40_MD5" is the
   * only enabled cipher suite, the client cannot request TLS 1.1 or
   * later, even though TLS 1.1 or later is enabled.  We need to create a
   * subset of the enabled protocols, called the active protocols, which
   * contains protocols appropriate to the list of enabled Ciphersuites.
   *
   * Return empty list instead of null if no active protocol versions.
   */
  ProtocolList getActiveProtocols() {
    if (activeProtocols == null) {
      ArrayList<ProtocolVersion> protocols = new ArrayList<>(4);
      for (ProtocolVersion protocol : enabledProtocols.collection()) {
        boolean found = false;
        for (CipherSuite suite : enabledCipherSuites.collection()) {
          if (suite.isAvailable()
              && suite.obsoleted > protocol.v
              && suite.supported <= protocol.v) {
            if (algorithmConstraints.permits(
                EnumSet.of(CryptoPrimitive.KEY_AGREEMENT), suite.name, null)) {
              protocols.add(protocol);
              found = true;
              break;
            } else if (debug != null && Debug.isOn("verbose")) {
              System.out.println("Ignoring disabled cipher suite: " + suite + " for " + protocol);
            }
          } else if (debug != null && Debug.isOn("verbose")) {
            System.out.println("Ignoring unsupported cipher suite: " + suite + " for " + protocol);
          }
        }
        if (!found && (debug != null) && Debug.isOn("handshake")) {
          System.out.println("No available cipher suite for " + protocol);
        }
      }
      activeProtocols = new ProtocolList(protocols);
    }

    return activeProtocols;
  }
Esempio n. 2
0
  /*
   * Sends a change cipher spec message and updates the write side
   * cipher state so that future messages use the just-negotiated spec.
   */
  void sendChangeCipherSpec(Finished mesg, boolean lastMessage) throws IOException {

    output.flush(); // i.e. handshake data

    /*
     * The write cipher state is protected by the connection write lock
     * so we must grab it while making the change. We also
     * make sure no writes occur between sending the ChangeCipherSpec
     * message, installing the new cipher state, and sending the
     * Finished message.
     *
     * We already hold SSLEngine/SSLSocket "this" by virtue
     * of this being called from the readRecord code.
     */
    OutputRecord r;
    if (conn != null) {
      r = new OutputRecord(Record.ct_change_cipher_spec);
    } else {
      r = new EngineOutputRecord(Record.ct_change_cipher_spec, engine);
    }

    r.setVersion(protocolVersion);
    r.write(1); // single byte of data

    if (conn != null) {
      conn.writeLock.lock();
      try {
        conn.writeRecord(r);
        conn.changeWriteCiphers();
        if (debug != null && Debug.isOn("handshake")) {
          mesg.print(System.out);
        }
        mesg.write(output);
        output.flush();
      } finally {
        conn.writeLock.unlock();
      }
    } else {
      synchronized (engine.writeLock) {
        engine.writeRecord((EngineOutputRecord) r);
        engine.changeWriteCiphers();
        if (debug != null && Debug.isOn("handshake")) {
          mesg.print(System.out);
        }
        mesg.write(output);

        if (lastMessage) {
          output.setFinishedMsg();
        }
        output.flush();
      }
    }
  }
 protected void sendRequest(PrintWriter out, String[] msg) {
   for (int i = 0; i < msg.length; i++) {
     if (Debug.get(Debug.Communication)) {
       System.err.println(">>>" + msg[i]);
     }
     out.print(msg[i] + crlf);
   }
   if (Debug.get(Debug.Communication)) {
     System.err.println(">>>");
   }
   out.print(crlf);
   out.flush();
 }
Esempio n. 4
0
  /**
   * Get the active cipher suites.
   *
   * <p>In TLS 1.1, many weak or vulnerable cipher suites were obsoleted, such as
   * TLS_RSA_EXPORT_WITH_RC4_40_MD5. The implementation MUST NOT negotiate these cipher suites in
   * TLS 1.1 or later mode.
   *
   * <p>Therefore, when the active protocols only include TLS 1.1 or later, the client cannot
   * request to negotiate those obsoleted cipher suites. That is, the obsoleted suites should not be
   * included in the client hello. So we need to create a subset of the enabled cipher suites, the
   * active cipher suites, which does not contain obsoleted cipher suites of the minimum active
   * protocol.
   *
   * <p>Return empty list instead of null if no active cipher suites.
   */
  CipherSuiteList getActiveCipherSuites() {
    if (activeCipherSuites == null) {
      if (activeProtocols == null) {
        activeProtocols = getActiveProtocols();
      }

      ArrayList<CipherSuite> suites = new ArrayList<>();
      if (!(activeProtocols.collection().isEmpty())
          && activeProtocols.min.v != ProtocolVersion.NONE.v) {
        for (CipherSuite suite : enabledCipherSuites.collection()) {
          if (suite.obsoleted > activeProtocols.min.v && suite.supported <= activeProtocols.max.v) {
            if (algorithmConstraints.permits(
                EnumSet.of(CryptoPrimitive.KEY_AGREEMENT), suite.name, null)) {
              suites.add(suite);
            }
          } else if (debug != null && Debug.isOn("verbose")) {
            if (suite.obsoleted <= activeProtocols.min.v) {
              System.out.println("Ignoring obsoleted cipher suite: " + suite);
            } else {
              System.out.println("Ignoring unsupported cipher suite: " + suite);
            }
          }
        }
      }
      activeCipherSuites = new CipherSuiteList(suites);
    }

    return activeCipherSuites;
  }
Esempio n. 5
0
  public SSLResult fingerprint() throws IOException, FingerprintError {

    SSLConfigCollector scc;

    scc = new SSLConfigCollector(host, port, si);
    scc.setCertValidator(cv);

    startDate = new Date();

    sslSupport = SSLResult.UNKNOWN;

    // If a delay is set, wait some time, except for
    // the first request
    if (!initial && (delay > 0)) {
      if (Debug.get(Debug.Delay)) {
        System.err.println("Delaying request.");
      }
      try {
        Thread.sleep(delay);
      } catch (InterruptedException e) {
        Thread.currentThread().interrupt();
      }
    }
    initial = false;

    try {
      scc.probe();
      sslSupport = SSLResult.SUPPORTED;
      sslSupportReason = null;
    } catch (NoSSLException e) {
      // This exception is thrown when the protocol support
      // for ssl is not available
      sslSupport = SSLResult.UNSUPPORTED;
      sslSupportReason = e.toString();
    } catch (FingerprintException e) {
      sslSupport = SSLResult.UNSUPPORTED;
      sslSupportReason = e.toString();
    } catch (IOException e) {
      sslSupport = SSLResult.UNKNOWN;
      sslSupportReason = e.toString();
    }
    endDate = new Date();

    protos = scc.getSupportedProtos();

    ProbeResult pres =
        new ProbeResult(
            host,
            port,
            startDate,
            endDate,
            sslSupport,
            sslSupportReason,
            scc.getServerCertificates(),
            scc.serverCertificateVerifies(),
            scc.serverCertNameMatch());

    pres.setProtosResult(protos);
    return pres;
  }
 public void setCredentials(String uid, String pass) {
   authID = uid;
   authPW = pass;
   if (Debug.get(Debug.SockInit)) {
     System.err.println("HttpProxySocketInitialiser: " + " uid " + authID + " pw " + authPW);
   }
 }
Esempio n. 7
0
  private void init(
      SSLContextImpl context,
      ProtocolList enabledProtocols,
      boolean needCertVerify,
      boolean isClient,
      ProtocolVersion activeProtocolVersion,
      boolean isInitialHandshake,
      boolean secureRenegotiation,
      byte[] clientVerifyData,
      byte[] serverVerifyData) {

    if (debug != null && Debug.isOn("handshake")) {
      System.out.println(
          "Allow unsafe renegotiation: "
              + allowUnsafeRenegotiation
              + "\nAllow legacy hello messages: "
              + allowLegacyHelloMessages
              + "\nIs initial handshake: "
              + isInitialHandshake
              + "\nIs secure renegotiation: "
              + secureRenegotiation);
    }

    this.sslContext = context;
    this.isClient = isClient;
    this.needCertVerify = needCertVerify;
    this.activeProtocolVersion = activeProtocolVersion;
    this.isInitialHandshake = isInitialHandshake;
    this.secureRenegotiation = secureRenegotiation;
    this.clientVerifyData = clientVerifyData;
    this.serverVerifyData = serverVerifyData;
    enableNewSession = true;
    invalidated = false;

    setCipherSuite(CipherSuite.C_NULL);
    setEnabledProtocols(enabledProtocols);

    if (conn != null) {
      algorithmConstraints = new SSLAlgorithmConstraints(conn, true);
    } else { // engine != null
      algorithmConstraints = new SSLAlgorithmConstraints(engine, true);
    }

    //
    // In addition to the connection state machine, controlling
    // how the connection deals with the different sorts of records
    // that get sent (notably handshake transitions!), there's
    // also a handshaking state machine that controls message
    // sequencing.
    //
    // It's a convenient artifact of the protocol that this can,
    // with only a couple of minor exceptions, be driven by the
    // type constant for the last message seen:  except for the
    // client's cert verify, those constants are in a convenient
    // order to drastically simplify state machine checking.
    //
    state = -2; // initialized but not activated
  }
Esempio n. 8
0
  /*
   * Used to kickstart the negotiation ... either writing a
   * ClientHello or a HelloRequest as appropriate, whichever
   * the subclass returns.  NOP if handshaking's already started.
   */
  void kickstart() throws IOException {
    if (state >= 0) {
      return;
    }

    HandshakeMessage m = getKickstartMessage();

    if (debug != null && Debug.isOn("handshake")) {
      m.print(System.out);
    }
    m.write(output);
    output.flush();

    state = m.messageType();
  }
  protected void prepareAuthorisation(String authVal) throws FingerprintError {

    if (authVal == null) {
      throw new HttpProxyError("Authentication " + "challenge missing");
    }
    int idx = authVal.indexOf(' ');
    String scheme = authVal.substring(0, idx).toLowerCase();
    String param;
    if (authVal.length() > idx) {
      param = authVal.substring(idx + 1);
    } else {
      param = "";
    }
    if (!scheme.equals("basic")) {
      throw new HttpProxyError("Auth scheme '" + scheme + "' not supported");
    }
    if (Debug.get(Debug.SockInit)) {
      System.err.println("prepareAuthorisation: " + "using scheme " + scheme);
    }
    authenticator = "Basic " + Base64.encode(authID + ":" + authPW);
    if (Debug.get(Debug.SockInit)) {
      System.err.println("prepareAuthorisation: auth=" + authenticator);
    }
  }
Esempio n. 10
0
/**
 * Handshaker ... processes handshake records from an SSL V3.0 data stream, handling all the details
 * of the handshake protocol.
 *
 * <p>Note that the real protocol work is done in two subclasses, the base class just provides the
 * control flow and key generation framework.
 *
 * @author David Brownell
 */
abstract class Handshaker {

  // protocol version being established using this Handshaker
  ProtocolVersion protocolVersion;

  // the currently active protocol version during a renegotiation
  ProtocolVersion activeProtocolVersion;

  // security parameters for secure renegotiation.
  boolean secureRenegotiation;
  byte[] clientVerifyData;
  byte[] serverVerifyData;

  // Is it an initial negotiation  or a renegotiation?
  boolean isInitialHandshake;

  // List of enabled protocols
  private ProtocolList enabledProtocols;

  // List of enabled CipherSuites
  private CipherSuiteList enabledCipherSuites;

  // The endpoint identification protocol
  String identificationProtocol;

  // The cryptographic algorithm constraints
  private AlgorithmConstraints algorithmConstraints = null;

  // Local supported signature and algorithms
  Collection<SignatureAndHashAlgorithm> localSupportedSignAlgs;

  // Peer supported signature and algorithms
  Collection<SignatureAndHashAlgorithm> peerSupportedSignAlgs;

  /*

  /*
   * List of active protocols
   *
   * Active protocols is a subset of enabled protocols, and will
   * contain only those protocols that have vaild cipher suites
   * enabled.
   */
  private ProtocolList activeProtocols;

  /*
   * List of active cipher suites
   *
   * Active cipher suites is a subset of enabled cipher suites, and will
   * contain only those cipher suites available for the active protocols.
   */
  private CipherSuiteList activeCipherSuites;

  private boolean isClient;
  private boolean needCertVerify;

  SSLSocketImpl conn = null;
  SSLEngineImpl engine = null;

  HandshakeHash handshakeHash;
  HandshakeInStream input;
  HandshakeOutStream output;
  int state;
  SSLContextImpl sslContext;
  RandomCookie clnt_random, svr_random;
  SSLSessionImpl session;

  // current CipherSuite. Never null, initially SSL_NULL_WITH_NULL_NULL
  CipherSuite cipherSuite;

  // current key exchange. Never null, initially K_NULL
  KeyExchange keyExchange;

  /* True if this session is being resumed (fast handshake) */
  boolean resumingSession;

  /* True if it's OK to start a new SSL session */
  boolean enableNewSession;

  // Temporary storage for the individual keys. Set by
  // calculateConnectionKeys() and cleared once the ciphers are
  // activated.
  private SecretKey clntWriteKey, svrWriteKey;
  private IvParameterSpec clntWriteIV, svrWriteIV;
  private SecretKey clntMacSecret, svrMacSecret;

  /*
   * Delegated task subsystem data structures.
   *
   * If thrown is set, we need to propagate this back immediately
   * on entry into processMessage().
   *
   * Data is protected by the SSLEngine.this lock.
   */
  private volatile boolean taskDelegated = false;
  private volatile DelegatedTask delegatedTask = null;
  private volatile Exception thrown = null;

  // Could probably use a java.util.concurrent.atomic.AtomicReference
  // here instead of using this lock.  Consider changing.
  private Object thrownLock = new Object();

  /* Class and subclass dynamic debugging support */
  static final Debug debug = Debug.getInstance("ssl");

  // By default, disable the unsafe legacy session renegotiation
  static final boolean allowUnsafeRenegotiation =
      Debug.getBooleanProperty("sun.security.ssl.allowUnsafeRenegotiation", false);

  // For maximum interoperability and backward compatibility, RFC 5746
  // allows server (or client) to accept ClientHello (or ServerHello)
  // message without the secure renegotiation_info extension or SCSV.
  //
  // For maximum security, RFC 5746 also allows server (or client) to
  // reject such message with a fatal "handshake_failure" alert.
  //
  // By default, allow such legacy hello messages.
  static final boolean allowLegacyHelloMessages =
      Debug.getBooleanProperty("sun.security.ssl.allowLegacyHelloMessages", true);

  // need to dispose the object when it is invalidated
  boolean invalidated;

  Handshaker(
      SSLSocketImpl c,
      SSLContextImpl context,
      ProtocolList enabledProtocols,
      boolean needCertVerify,
      boolean isClient,
      ProtocolVersion activeProtocolVersion,
      boolean isInitialHandshake,
      boolean secureRenegotiation,
      byte[] clientVerifyData,
      byte[] serverVerifyData) {
    this.conn = c;
    init(
        context,
        enabledProtocols,
        needCertVerify,
        isClient,
        activeProtocolVersion,
        isInitialHandshake,
        secureRenegotiation,
        clientVerifyData,
        serverVerifyData);
  }

  Handshaker(
      SSLEngineImpl engine,
      SSLContextImpl context,
      ProtocolList enabledProtocols,
      boolean needCertVerify,
      boolean isClient,
      ProtocolVersion activeProtocolVersion,
      boolean isInitialHandshake,
      boolean secureRenegotiation,
      byte[] clientVerifyData,
      byte[] serverVerifyData) {
    this.engine = engine;
    init(
        context,
        enabledProtocols,
        needCertVerify,
        isClient,
        activeProtocolVersion,
        isInitialHandshake,
        secureRenegotiation,
        clientVerifyData,
        serverVerifyData);
  }

  private void init(
      SSLContextImpl context,
      ProtocolList enabledProtocols,
      boolean needCertVerify,
      boolean isClient,
      ProtocolVersion activeProtocolVersion,
      boolean isInitialHandshake,
      boolean secureRenegotiation,
      byte[] clientVerifyData,
      byte[] serverVerifyData) {

    if (debug != null && Debug.isOn("handshake")) {
      System.out.println(
          "Allow unsafe renegotiation: "
              + allowUnsafeRenegotiation
              + "\nAllow legacy hello messages: "
              + allowLegacyHelloMessages
              + "\nIs initial handshake: "
              + isInitialHandshake
              + "\nIs secure renegotiation: "
              + secureRenegotiation);
    }

    this.sslContext = context;
    this.isClient = isClient;
    this.needCertVerify = needCertVerify;
    this.activeProtocolVersion = activeProtocolVersion;
    this.isInitialHandshake = isInitialHandshake;
    this.secureRenegotiation = secureRenegotiation;
    this.clientVerifyData = clientVerifyData;
    this.serverVerifyData = serverVerifyData;
    enableNewSession = true;
    invalidated = false;

    setCipherSuite(CipherSuite.C_NULL);
    setEnabledProtocols(enabledProtocols);

    if (conn != null) {
      algorithmConstraints = new SSLAlgorithmConstraints(conn, true);
    } else { // engine != null
      algorithmConstraints = new SSLAlgorithmConstraints(engine, true);
    }

    //
    // In addition to the connection state machine, controlling
    // how the connection deals with the different sorts of records
    // that get sent (notably handshake transitions!), there's
    // also a handshaking state machine that controls message
    // sequencing.
    //
    // It's a convenient artifact of the protocol that this can,
    // with only a couple of minor exceptions, be driven by the
    // type constant for the last message seen:  except for the
    // client's cert verify, those constants are in a convenient
    // order to drastically simplify state machine checking.
    //
    state = -2; // initialized but not activated
  }

  /*
   * Reroutes calls to the SSLSocket or SSLEngine (*SE).
   *
   * We could have also done it by extra classes
   * and letting them override, but this seemed much
   * less involved.
   */
  void fatalSE(byte b, String diagnostic) throws IOException {
    fatalSE(b, diagnostic, null);
  }

  void fatalSE(byte b, Throwable cause) throws IOException {
    fatalSE(b, null, cause);
  }

  void fatalSE(byte b, String diagnostic, Throwable cause) throws IOException {
    if (conn != null) {
      conn.fatal(b, diagnostic, cause);
    } else {
      engine.fatal(b, diagnostic, cause);
    }
  }

  void warningSE(byte b) {
    if (conn != null) {
      conn.warning(b);
    } else {
      engine.warning(b);
    }
  }

  String getRawHostnameSE() {
    if (conn != null) {
      return conn.getRawHostname();
    } else {
      return engine.getPeerHost();
    }
  }

  String getHostSE() {
    if (conn != null) {
      return conn.getHost();
    } else {
      return engine.getPeerHost();
    }
  }

  String getHostAddressSE() {
    if (conn != null) {
      return conn.getInetAddress().getHostAddress();
    } else {
      /*
       * This is for caching only, doesn't matter that's is really
       * a hostname.  The main thing is that it doesn't do
       * a reverse DNS lookup, potentially slowing things down.
       */
      return engine.getPeerHost();
    }
  }

  boolean isLoopbackSE() {
    if (conn != null) {
      return conn.getInetAddress().isLoopbackAddress();
    } else {
      return false;
    }
  }

  int getPortSE() {
    if (conn != null) {
      return conn.getPort();
    } else {
      return engine.getPeerPort();
    }
  }

  int getLocalPortSE() {
    if (conn != null) {
      return conn.getLocalPort();
    } else {
      return -1;
    }
  }

  AccessControlContext getAccSE() {
    if (conn != null) {
      return conn.getAcc();
    } else {
      return engine.getAcc();
    }
  }

  private void setVersionSE(ProtocolVersion protocolVersion) {
    if (conn != null) {
      conn.setVersion(protocolVersion);
    } else {
      engine.setVersion(protocolVersion);
    }
  }

  /**
   * Set the active protocol version and propagate it to the SSLSocket and our handshake streams.
   * Called from ClientHandshaker and ServerHandshaker with the negotiated protocol version.
   */
  void setVersion(ProtocolVersion protocolVersion) {
    this.protocolVersion = protocolVersion;
    setVersionSE(protocolVersion);

    output.r.setVersion(protocolVersion);
  }

  /**
   * Set the enabled protocols. Called from the constructor or
   * SSLSocketImpl/SSLEngineImpl.setEnabledProtocols() (if the handshake is not yet in progress).
   */
  void setEnabledProtocols(ProtocolList enabledProtocols) {
    activeCipherSuites = null;
    activeProtocols = null;

    this.enabledProtocols = enabledProtocols;
  }

  /**
   * Set the enabled cipher suites. Called from SSLSocketImpl/SSLEngineImpl.setEnabledCipherSuites()
   * (if the handshake is not yet in progress).
   */
  void setEnabledCipherSuites(CipherSuiteList enabledCipherSuites) {
    activeCipherSuites = null;
    activeProtocols = null;
    this.enabledCipherSuites = enabledCipherSuites;
  }

  /**
   * Set the algorithm constraints. Called from the constructor or
   * SSLSocketImpl/SSLEngineImpl.setAlgorithmConstraints() (if the handshake is not yet in
   * progress).
   */
  void setAlgorithmConstraints(AlgorithmConstraints algorithmConstraints) {
    activeCipherSuites = null;
    activeProtocols = null;

    this.algorithmConstraints = new SSLAlgorithmConstraints(algorithmConstraints);
    this.localSupportedSignAlgs = null;
  }

  Collection<SignatureAndHashAlgorithm> getLocalSupportedSignAlgs() {
    if (localSupportedSignAlgs == null) {
      localSupportedSignAlgs =
          SignatureAndHashAlgorithm.getSupportedAlgorithms(algorithmConstraints);
    }

    return localSupportedSignAlgs;
  }

  void setPeerSupportedSignAlgs(Collection<SignatureAndHashAlgorithm> algorithms) {
    peerSupportedSignAlgs = new ArrayList<SignatureAndHashAlgorithm>(algorithms);
  }

  Collection<SignatureAndHashAlgorithm> getPeerSupportedSignAlgs() {
    return peerSupportedSignAlgs;
  }

  /**
   * Set the identification protocol. Called from the constructor or
   * SSLSocketImpl/SSLEngineImpl.setIdentificationProtocol() (if the handshake is not yet in
   * progress).
   */
  void setIdentificationProtocol(String protocol) {
    this.identificationProtocol = protocol;
  }

  /**
   * Prior to handshaking, activate the handshake and initialize the version, input stream and
   * output stream.
   */
  void activate(ProtocolVersion helloVersion) throws IOException {
    if (activeProtocols == null) {
      activeProtocols = getActiveProtocols();
    }

    if (activeProtocols.collection().isEmpty() || activeProtocols.max.v == ProtocolVersion.NONE.v) {
      throw new SSLHandshakeException("No appropriate protocol");
    }

    if (activeCipherSuites == null) {
      activeCipherSuites = getActiveCipherSuites();
    }

    if (activeCipherSuites.collection().isEmpty()) {
      throw new SSLHandshakeException("No appropriate cipher suite");
    }

    // temporary protocol version until the actual protocol version
    // is negotiated in the Hello exchange. This affects the record
    // version we sent with the ClientHello.
    if (!isInitialHandshake) {
      protocolVersion = activeProtocolVersion;
    } else {
      protocolVersion = activeProtocols.max;
    }

    if (helloVersion == null || helloVersion.v == ProtocolVersion.NONE.v) {
      helloVersion = activeProtocols.helloVersion;
    }

    // We accumulate digests of the handshake messages so that
    // we can read/write CertificateVerify and Finished messages,
    // getting assurance against some particular active attacks.
    Set<String> localSupportedHashAlgorithms =
        SignatureAndHashAlgorithm.getHashAlgorithmNames(getLocalSupportedSignAlgs());
    handshakeHash = new HandshakeHash(!isClient, needCertVerify, localSupportedHashAlgorithms);

    // Generate handshake input/output stream.
    input = new HandshakeInStream(handshakeHash);
    if (conn != null) {
      output = new HandshakeOutStream(protocolVersion, helloVersion, handshakeHash, conn);
      conn.getAppInputStream().r.setHandshakeHash(handshakeHash);
      conn.getAppInputStream().r.setHelloVersion(helloVersion);
      conn.getAppOutputStream().r.setHelloVersion(helloVersion);
    } else {
      output = new HandshakeOutStream(protocolVersion, helloVersion, handshakeHash, engine);
      engine.inputRecord.setHandshakeHash(handshakeHash);
      engine.inputRecord.setHelloVersion(helloVersion);
      engine.outputRecord.setHelloVersion(helloVersion);
    }

    // move state to activated
    state = -1;
  }

  /**
   * Set cipherSuite and keyExchange to the given CipherSuite. Does not perform any verification
   * that this is a valid selection, this must be done before calling this method.
   */
  void setCipherSuite(CipherSuite s) {
    this.cipherSuite = s;
    this.keyExchange = s.keyExchange;
  }

  /**
   * Check if the given ciphersuite is enabled and available. Does not check if the required server
   * certificates are available.
   */
  boolean isNegotiable(CipherSuite s) {
    if (activeCipherSuites == null) {
      activeCipherSuites = getActiveCipherSuites();
    }

    return activeCipherSuites.contains(s) && s.isNegotiable();
  }

  /** Check if the given protocol version is enabled and available. */
  boolean isNegotiable(ProtocolVersion protocolVersion) {
    if (activeProtocols == null) {
      activeProtocols = getActiveProtocols();
    }

    return activeProtocols.contains(protocolVersion);
  }

  /**
   * Select a protocol version from the list. Called from ServerHandshaker to negotiate protocol
   * version.
   *
   * <p>Return the lower of the protocol version suggested in the clien hello and the highest
   * supported by the server.
   */
  ProtocolVersion selectProtocolVersion(ProtocolVersion protocolVersion) {
    if (activeProtocols == null) {
      activeProtocols = getActiveProtocols();
    }

    return activeProtocols.selectProtocolVersion(protocolVersion);
  }

  /**
   * Get the active cipher suites.
   *
   * <p>In TLS 1.1, many weak or vulnerable cipher suites were obsoleted, such as
   * TLS_RSA_EXPORT_WITH_RC4_40_MD5. The implementation MUST NOT negotiate these cipher suites in
   * TLS 1.1 or later mode.
   *
   * <p>Therefore, when the active protocols only include TLS 1.1 or later, the client cannot
   * request to negotiate those obsoleted cipher suites. That is, the obsoleted suites should not be
   * included in the client hello. So we need to create a subset of the enabled cipher suites, the
   * active cipher suites, which does not contain obsoleted cipher suites of the minimum active
   * protocol.
   *
   * <p>Return empty list instead of null if no active cipher suites.
   */
  CipherSuiteList getActiveCipherSuites() {
    if (activeCipherSuites == null) {
      if (activeProtocols == null) {
        activeProtocols = getActiveProtocols();
      }

      ArrayList<CipherSuite> suites = new ArrayList<>();
      if (!(activeProtocols.collection().isEmpty())
          && activeProtocols.min.v != ProtocolVersion.NONE.v) {
        for (CipherSuite suite : enabledCipherSuites.collection()) {
          if (suite.obsoleted > activeProtocols.min.v && suite.supported <= activeProtocols.max.v) {
            if (algorithmConstraints.permits(
                EnumSet.of(CryptoPrimitive.KEY_AGREEMENT), suite.name, null)) {
              suites.add(suite);
            }
          } else if (debug != null && Debug.isOn("verbose")) {
            if (suite.obsoleted <= activeProtocols.min.v) {
              System.out.println("Ignoring obsoleted cipher suite: " + suite);
            } else {
              System.out.println("Ignoring unsupported cipher suite: " + suite);
            }
          }
        }
      }
      activeCipherSuites = new CipherSuiteList(suites);
    }

    return activeCipherSuites;
  }

  /*
   * Get the active protocol versions.
   *
   * In TLS 1.1, many weak or vulnerable cipher suites were obsoleted,
   * such as TLS_RSA_EXPORT_WITH_RC4_40_MD5. The implementation MUST NOT
   * negotiate these cipher suites in TLS 1.1 or later mode.
   *
   * For example, if "TLS_RSA_EXPORT_WITH_RC4_40_MD5" is the
   * only enabled cipher suite, the client cannot request TLS 1.1 or
   * later, even though TLS 1.1 or later is enabled.  We need to create a
   * subset of the enabled protocols, called the active protocols, which
   * contains protocols appropriate to the list of enabled Ciphersuites.
   *
   * Return empty list instead of null if no active protocol versions.
   */
  ProtocolList getActiveProtocols() {
    if (activeProtocols == null) {
      ArrayList<ProtocolVersion> protocols = new ArrayList<>(4);
      for (ProtocolVersion protocol : enabledProtocols.collection()) {
        boolean found = false;
        for (CipherSuite suite : enabledCipherSuites.collection()) {
          if (suite.isAvailable()
              && suite.obsoleted > protocol.v
              && suite.supported <= protocol.v) {
            if (algorithmConstraints.permits(
                EnumSet.of(CryptoPrimitive.KEY_AGREEMENT), suite.name, null)) {
              protocols.add(protocol);
              found = true;
              break;
            } else if (debug != null && Debug.isOn("verbose")) {
              System.out.println("Ignoring disabled cipher suite: " + suite + " for " + protocol);
            }
          } else if (debug != null && Debug.isOn("verbose")) {
            System.out.println("Ignoring unsupported cipher suite: " + suite + " for " + protocol);
          }
        }
        if (!found && (debug != null) && Debug.isOn("handshake")) {
          System.out.println("No available cipher suite for " + protocol);
        }
      }
      activeProtocols = new ProtocolList(protocols);
    }

    return activeProtocols;
  }

  /**
   * As long as handshaking has not activated, we can change whether session creations are allowed.
   *
   * <p>Callers should do their own checking if handshaking has activated.
   */
  void setEnableSessionCreation(boolean newSessions) {
    enableNewSession = newSessions;
  }

  /** Create a new read cipher and return it to caller. */
  CipherBox newReadCipher() throws NoSuchAlgorithmException {
    BulkCipher cipher = cipherSuite.cipher;
    CipherBox box;
    if (isClient) {
      box =
          cipher.newCipher(
              protocolVersion, svrWriteKey, svrWriteIV, sslContext.getSecureRandom(), false);
      svrWriteKey = null;
      svrWriteIV = null;
    } else {
      box =
          cipher.newCipher(
              protocolVersion, clntWriteKey, clntWriteIV, sslContext.getSecureRandom(), false);
      clntWriteKey = null;
      clntWriteIV = null;
    }
    return box;
  }

  /** Create a new write cipher and return it to caller. */
  CipherBox newWriteCipher() throws NoSuchAlgorithmException {
    BulkCipher cipher = cipherSuite.cipher;
    CipherBox box;
    if (isClient) {
      box =
          cipher.newCipher(
              protocolVersion, clntWriteKey, clntWriteIV, sslContext.getSecureRandom(), true);
      clntWriteKey = null;
      clntWriteIV = null;
    } else {
      box =
          cipher.newCipher(
              protocolVersion, svrWriteKey, svrWriteIV, sslContext.getSecureRandom(), true);
      svrWriteKey = null;
      svrWriteIV = null;
    }
    return box;
  }

  /** Create a new read MAC and return it to caller. */
  MAC newReadMAC() throws NoSuchAlgorithmException, InvalidKeyException {
    MacAlg macAlg = cipherSuite.macAlg;
    MAC mac;
    if (isClient) {
      mac = macAlg.newMac(protocolVersion, svrMacSecret);
      svrMacSecret = null;
    } else {
      mac = macAlg.newMac(protocolVersion, clntMacSecret);
      clntMacSecret = null;
    }
    return mac;
  }

  /** Create a new write MAC and return it to caller. */
  MAC newWriteMAC() throws NoSuchAlgorithmException, InvalidKeyException {
    MacAlg macAlg = cipherSuite.macAlg;
    MAC mac;
    if (isClient) {
      mac = macAlg.newMac(protocolVersion, clntMacSecret);
      clntMacSecret = null;
    } else {
      mac = macAlg.newMac(protocolVersion, svrMacSecret);
      svrMacSecret = null;
    }
    return mac;
  }

  /*
   * Returns true iff the handshake sequence is done, so that
   * this freshly created session can become the current one.
   */
  boolean isDone() {
    return state == HandshakeMessage.ht_finished;
  }

  /*
   * Returns the session which was created through this
   * handshake sequence ... should be called after isDone()
   * returns true.
   */
  SSLSessionImpl getSession() {
    return session;
  }

  /*
   * Set the handshake session
   */
  void setHandshakeSessionSE(SSLSessionImpl handshakeSession) {
    if (conn != null) {
      conn.setHandshakeSession(handshakeSession);
    } else {
      engine.setHandshakeSession(handshakeSession);
    }
  }

  /*
   * Returns true if renegotiation is in use for this connection.
   */
  boolean isSecureRenegotiation() {
    return secureRenegotiation;
  }

  /*
   * Returns the verify_data from the Finished message sent by the client.
   */
  byte[] getClientVerifyData() {
    return clientVerifyData;
  }

  /*
   * Returns the verify_data from the Finished message sent by the server.
   */
  byte[] getServerVerifyData() {
    return serverVerifyData;
  }

  /*
   * This routine is fed SSL handshake records when they become available,
   * and processes messages found therein.
   */
  void process_record(InputRecord r, boolean expectingFinished) throws IOException {

    checkThrown();

    /*
     * Store the incoming handshake data, then see if we can
     * now process any completed handshake messages
     */
    input.incomingRecord(r);

    /*
     * We don't need to create a separate delegatable task
     * for finished messages.
     */
    if ((conn != null) || expectingFinished) {
      processLoop();
    } else {
      delegateTask(
          new PrivilegedExceptionAction<Void>() {
            public Void run() throws Exception {
              processLoop();
              return null;
            }
          });
    }
  }

  /*
   * On input, we hash messages one at a time since servers may need
   * to access an intermediate hash to validate a CertificateVerify
   * message.
   *
   * Note that many handshake messages can come in one record (and often
   * do, to reduce network resource utilization), and one message can also
   * require multiple records (e.g. very large Certificate messages).
   */
  void processLoop() throws IOException {

    // need to read off 4 bytes at least to get the handshake
    // message type and length.
    while (input.available() >= 4) {
      byte messageType;
      int messageLen;

      /*
       * See if we can read the handshake message header, and
       * then the entire handshake message.  If not, wait till
       * we can read and process an entire message.
       */
      input.mark(4);

      messageType = (byte) input.getInt8();
      messageLen = input.getInt24();

      if (input.available() < messageLen) {
        input.reset();
        return;
      }

      /*
       * Process the messsage.  We require
       * that processMessage() consumes the entire message.  In
       * lieu of explicit error checks (how?!) we assume that the
       * data will look like garbage on encoding/processing errors,
       * and that other protocol code will detect such errors.
       *
       * Note that digesting is normally deferred till after the
       * message has been processed, though to process at least the
       * client's Finished message (i.e. send the server's) we need
       * to acccelerate that digesting.
       *
       * Also, note that hello request messages are never hashed;
       * that includes the hello request header, too.
       */
      if (messageType == HandshakeMessage.ht_hello_request) {
        input.reset();
        processMessage(messageType, messageLen);
        input.ignore(4 + messageLen);
      } else {
        input.mark(messageLen);
        processMessage(messageType, messageLen);
        input.digestNow();
      }
    }
  }

  /**
   * Returns true iff the handshaker has been activated.
   *
   * <p>In activated state, the handshaker may not send any messages out.
   */
  boolean activated() {
    return state >= -1;
  }

  /** Returns true iff the handshaker has sent any messages. */
  boolean started() {
    return state >= 0; // 0: HandshakeMessage.ht_hello_request
    // 1: HandshakeMessage.ht_client_hello
  }

  /*
   * Used to kickstart the negotiation ... either writing a
   * ClientHello or a HelloRequest as appropriate, whichever
   * the subclass returns.  NOP if handshaking's already started.
   */
  void kickstart() throws IOException {
    if (state >= 0) {
      return;
    }

    HandshakeMessage m = getKickstartMessage();

    if (debug != null && Debug.isOn("handshake")) {
      m.print(System.out);
    }
    m.write(output);
    output.flush();

    state = m.messageType();
  }

  /**
   * Both client and server modes can start handshaking; but the message they send to do so is
   * different.
   */
  abstract HandshakeMessage getKickstartMessage() throws SSLException;

  /*
   * Client and Server side protocols are each driven though this
   * call, which processes a single message and drives the appropriate
   * side of the protocol state machine (depending on the subclass).
   */
  abstract void processMessage(byte messageType, int messageLen) throws IOException;

  /*
   * Most alerts in the protocol relate to handshaking problems.
   * Alerts are detected as the connection reads data.
   */
  abstract void handshakeAlert(byte description) throws SSLProtocolException;

  /*
   * Sends a change cipher spec message and updates the write side
   * cipher state so that future messages use the just-negotiated spec.
   */
  void sendChangeCipherSpec(Finished mesg, boolean lastMessage) throws IOException {

    output.flush(); // i.e. handshake data

    /*
     * The write cipher state is protected by the connection write lock
     * so we must grab it while making the change. We also
     * make sure no writes occur between sending the ChangeCipherSpec
     * message, installing the new cipher state, and sending the
     * Finished message.
     *
     * We already hold SSLEngine/SSLSocket "this" by virtue
     * of this being called from the readRecord code.
     */
    OutputRecord r;
    if (conn != null) {
      r = new OutputRecord(Record.ct_change_cipher_spec);
    } else {
      r = new EngineOutputRecord(Record.ct_change_cipher_spec, engine);
    }

    r.setVersion(protocolVersion);
    r.write(1); // single byte of data

    if (conn != null) {
      conn.writeLock.lock();
      try {
        conn.writeRecord(r);
        conn.changeWriteCiphers();
        if (debug != null && Debug.isOn("handshake")) {
          mesg.print(System.out);
        }
        mesg.write(output);
        output.flush();
      } finally {
        conn.writeLock.unlock();
      }
    } else {
      synchronized (engine.writeLock) {
        engine.writeRecord((EngineOutputRecord) r);
        engine.changeWriteCiphers();
        if (debug != null && Debug.isOn("handshake")) {
          mesg.print(System.out);
        }
        mesg.write(output);

        if (lastMessage) {
          output.setFinishedMsg();
        }
        output.flush();
      }
    }
  }

  /*
   * Single access point to key calculation logic.  Given the
   * pre-master secret and the nonces from client and server,
   * produce all the keying material to be used.
   */
  void calculateKeys(SecretKey preMasterSecret, ProtocolVersion version) {
    SecretKey master = calculateMasterSecret(preMasterSecret, version);
    session.setMasterSecret(master);
    calculateConnectionKeys(master);
  }

  /*
   * Calculate the master secret from its various components.  This is
   * used for key exchange by all cipher suites.
   *
   * The master secret is the catenation of three MD5 hashes, each
   * consisting of the pre-master secret and a SHA1 hash.  Those three
   * SHA1 hashes are of (different) constant strings, the pre-master
   * secret, and the nonces provided by the client and the server.
   */
  private SecretKey calculateMasterSecret(
      SecretKey preMasterSecret, ProtocolVersion requestedVersion) {

    if (debug != null && Debug.isOn("keygen")) {
      HexDumpEncoder dump = new HexDumpEncoder();

      System.out.println("SESSION KEYGEN:");

      System.out.println("PreMaster Secret:");
      printHex(dump, preMasterSecret.getEncoded());

      // Nonces are dumped with connection keygen, no
      // benefit to doing it twice
    }

    // What algs/params do we need to use?
    String masterAlg;
    PRF prf;

    if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
      masterAlg = "SunTls12MasterSecret";
      prf = cipherSuite.prfAlg;
    } else {
      masterAlg = "SunTlsMasterSecret";
      prf = P_NONE;
    }

    String prfHashAlg = prf.getPRFHashAlg();
    int prfHashLength = prf.getPRFHashLength();
    int prfBlockSize = prf.getPRFBlockSize();

    TlsMasterSecretParameterSpec spec =
        new TlsMasterSecretParameterSpec(
            preMasterSecret,
            protocolVersion.major,
            protocolVersion.minor,
            clnt_random.random_bytes,
            svr_random.random_bytes,
            prfHashAlg,
            prfHashLength,
            prfBlockSize);

    SecretKey masterSecret;
    try {
      KeyGenerator kg = JsseJce.getKeyGenerator(masterAlg);
      kg.init(spec);
      masterSecret = kg.generateKey();
    } catch (GeneralSecurityException e) {
      // For RSA premaster secrets, do not signal a protocol error
      // due to the Bleichenbacher attack. See comments further down.
      if (!preMasterSecret.getAlgorithm().equals("TlsRsaPremasterSecret")) {
        throw new ProviderException(e);
      }

      if (debug != null && Debug.isOn("handshake")) {
        System.out.println("RSA master secret generation error:");
        e.printStackTrace(System.out);
        System.out.println("Generating new random premaster secret");
      }

      if (requestedVersion != null) {
        preMasterSecret = RSAClientKeyExchange.generateDummySecret(requestedVersion);
      } else {
        preMasterSecret = RSAClientKeyExchange.generateDummySecret(protocolVersion);
      }

      // recursive call with new premaster secret
      return calculateMasterSecret(preMasterSecret, null);
    }

    // if no version check requested (client side handshake), or version
    // information is not available (not an RSA premaster secret),
    // return master secret immediately.
    if ((requestedVersion == null) || !(masterSecret instanceof TlsMasterSecret)) {
      return masterSecret;
    }

    // we have checked the ClientKeyExchange message when reading TLS
    // record, the following check is necessary to ensure that
    // JCE provider does not ignore the checking, or the previous
    // checking process bypassed the premaster secret version checking.
    TlsMasterSecret tlsKey = (TlsMasterSecret) masterSecret;
    int major = tlsKey.getMajorVersion();
    int minor = tlsKey.getMinorVersion();
    if ((major < 0) || (minor < 0)) {
      return masterSecret;
    }

    // check if the premaster secret version is ok
    // the specification says that it must be the maximum version supported
    // by the client from its ClientHello message. However, many
    // implementations send the negotiated version, so accept both
    // for SSL v3.0 and TLS v1.0.
    // NOTE that we may be comparing two unsupported version numbers, which
    // is why we cannot use object reference equality in this special case.
    ProtocolVersion premasterVersion = ProtocolVersion.valueOf(major, minor);
    boolean versionMismatch = (premasterVersion.v != requestedVersion.v);

    /*
     * we never checked the client_version in server side
     * for TLS v1.0 and SSL v3.0. For compatibility, we
     * maintain this behavior.
     */
    if (versionMismatch && requestedVersion.v <= ProtocolVersion.TLS10.v) {
      versionMismatch = (premasterVersion.v != protocolVersion.v);
    }

    if (versionMismatch == false) {
      // check passed, return key
      return masterSecret;
    }

    // Due to the Bleichenbacher attack, do not signal a protocol error.
    // Generate a random premaster secret and continue with the handshake,
    // which will fail when verifying the finished messages.
    // For more information, see comments in PreMasterSecret.
    if (debug != null && Debug.isOn("handshake")) {
      System.out.println(
          "RSA PreMasterSecret version error: expected"
              + protocolVersion
              + " or "
              + requestedVersion
              + ", decrypted: "
              + premasterVersion);
      System.out.println("Generating new random premaster secret");
    }
    preMasterSecret = RSAClientKeyExchange.generateDummySecret(requestedVersion);

    // recursive call with new premaster secret
    return calculateMasterSecret(preMasterSecret, null);
  }

  /*
   * Calculate the keys needed for this connection, once the session's
   * master secret has been calculated.  Uses the master key and nonces;
   * the amount of keying material generated is a function of the cipher
   * suite that's been negotiated.
   *
   * This gets called both on the "full handshake" (where we exchanged
   * a premaster secret and started a new session) as well as on the
   * "fast handshake" (where we just resumed a pre-existing session).
   */
  void calculateConnectionKeys(SecretKey masterKey) {
    /*
     * For both the read and write sides of the protocol, we use the
     * master to generate MAC secrets and cipher keying material.  Block
     * ciphers need initialization vectors, which we also generate.
     *
     * First we figure out how much keying material is needed.
     */
    int hashSize = cipherSuite.macAlg.size;
    boolean is_exportable = cipherSuite.exportable;
    BulkCipher cipher = cipherSuite.cipher;
    int expandedKeySize = is_exportable ? cipher.expandedKeySize : 0;

    // Which algs/params do we need to use?
    String keyMaterialAlg;
    PRF prf;

    if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
      keyMaterialAlg = "SunTls12KeyMaterial";
      prf = cipherSuite.prfAlg;
    } else {
      keyMaterialAlg = "SunTlsKeyMaterial";
      prf = P_NONE;
    }

    String prfHashAlg = prf.getPRFHashAlg();
    int prfHashLength = prf.getPRFHashLength();
    int prfBlockSize = prf.getPRFBlockSize();

    TlsKeyMaterialParameterSpec spec =
        new TlsKeyMaterialParameterSpec(
            masterKey,
            protocolVersion.major,
            protocolVersion.minor,
            clnt_random.random_bytes,
            svr_random.random_bytes,
            cipher.algorithm,
            cipher.keySize,
            expandedKeySize,
            cipher.ivSize,
            hashSize,
            prfHashAlg,
            prfHashLength,
            prfBlockSize);

    try {
      KeyGenerator kg = JsseJce.getKeyGenerator(keyMaterialAlg);
      kg.init(spec);
      TlsKeyMaterialSpec keySpec = (TlsKeyMaterialSpec) kg.generateKey();

      clntWriteKey = keySpec.getClientCipherKey();
      svrWriteKey = keySpec.getServerCipherKey();

      // Return null if IVs are not supposed to be generated.
      // e.g. TLS 1.1+.
      clntWriteIV = keySpec.getClientIv();
      svrWriteIV = keySpec.getServerIv();

      clntMacSecret = keySpec.getClientMacKey();
      svrMacSecret = keySpec.getServerMacKey();
    } catch (GeneralSecurityException e) {
      throw new ProviderException(e);
    }

    //
    // Dump the connection keys as they're generated.
    //
    if (debug != null && Debug.isOn("keygen")) {
      synchronized (System.out) {
        HexDumpEncoder dump = new HexDumpEncoder();

        System.out.println("CONNECTION KEYGEN:");

        // Inputs:
        System.out.println("Client Nonce:");
        printHex(dump, clnt_random.random_bytes);
        System.out.println("Server Nonce:");
        printHex(dump, svr_random.random_bytes);
        System.out.println("Master Secret:");
        printHex(dump, masterKey.getEncoded());

        // Outputs:
        System.out.println("Client MAC write Secret:");
        printHex(dump, clntMacSecret.getEncoded());
        System.out.println("Server MAC write Secret:");
        printHex(dump, svrMacSecret.getEncoded());

        if (clntWriteKey != null) {
          System.out.println("Client write key:");
          printHex(dump, clntWriteKey.getEncoded());
          System.out.println("Server write key:");
          printHex(dump, svrWriteKey.getEncoded());
        } else {
          System.out.println("... no encryption keys used");
        }

        if (clntWriteIV != null) {
          System.out.println("Client write IV:");
          printHex(dump, clntWriteIV.getIV());
          System.out.println("Server write IV:");
          printHex(dump, svrWriteIV.getIV());
        } else {
          if (protocolVersion.v >= ProtocolVersion.TLS11.v) {
            System.out.println("... no IV derived for this protocol");
          } else {
            System.out.println("... no IV used for this cipher");
          }
        }
        System.out.flush();
      }
    }
  }

  private static void printHex(HexDumpEncoder dump, byte[] bytes) {
    if (bytes == null) {
      System.out.println("(key bytes not available)");
    } else {
      try {
        dump.encodeBuffer(bytes, System.out);
      } catch (IOException e) {
        // just for debugging, ignore this
      }
    }
  }

  /**
   * Throw an SSLException with the specified message and cause. Shorthand until a new SSLException
   * constructor is added. This method never returns.
   */
  static void throwSSLException(String msg, Throwable cause) throws SSLException {
    SSLException e = new SSLException(msg);
    e.initCause(cause);
    throw e;
  }

  /*
   * Implement a simple task delegator.
   *
   * We are currently implementing this as a single delegator, may
   * try for parallel tasks later.  Client Authentication could
   * benefit from this, where ClientKeyExchange/CertificateVerify
   * could be carried out in parallel.
   */
  class DelegatedTask<E> implements Runnable {

    private PrivilegedExceptionAction<E> pea;

    DelegatedTask(PrivilegedExceptionAction<E> pea) {
      this.pea = pea;
    }

    public void run() {
      synchronized (engine) {
        try {
          AccessController.doPrivileged(pea, engine.getAcc());
        } catch (PrivilegedActionException pae) {
          thrown = pae.getException();
        } catch (RuntimeException rte) {
          thrown = rte;
        }
        delegatedTask = null;
        taskDelegated = false;
      }
    }
  }

  private <T> void delegateTask(PrivilegedExceptionAction<T> pea) {
    delegatedTask = new DelegatedTask<T>(pea);
    taskDelegated = false;
    thrown = null;
  }

  DelegatedTask getTask() {
    if (!taskDelegated) {
      taskDelegated = true;
      return delegatedTask;
    } else {
      return null;
    }
  }

  /*
   * See if there are any tasks which need to be delegated
   *
   * Locked by SSLEngine.this.
   */
  boolean taskOutstanding() {
    return (delegatedTask != null);
  }

  /*
   * The previous caller failed for some reason, report back the
   * Exception.  We won't worry about Error's.
   *
   * Locked by SSLEngine.this.
   */
  void checkThrown() throws SSLException {
    synchronized (thrownLock) {
      if (thrown != null) {

        String msg = thrown.getMessage();

        if (msg == null) {
          msg = "Delegated task threw Exception/Error";
        }

        /*
         * See what the underlying type of exception is.  We should
         * throw the same thing.  Chain thrown to the new exception.
         */
        Exception e = thrown;
        thrown = null;

        if (e instanceof RuntimeException) {
          throw (RuntimeException) new RuntimeException(msg).initCause(e);
        } else if (e instanceof SSLHandshakeException) {
          throw (SSLHandshakeException) new SSLHandshakeException(msg).initCause(e);
        } else if (e instanceof SSLKeyException) {
          throw (SSLKeyException) new SSLKeyException(msg).initCause(e);
        } else if (e instanceof SSLPeerUnverifiedException) {
          throw (SSLPeerUnverifiedException) new SSLPeerUnverifiedException(msg).initCause(e);
        } else if (e instanceof SSLProtocolException) {
          throw (SSLProtocolException) new SSLProtocolException(msg).initCause(e);
        } else {
          /*
           * If it's SSLException or any other Exception,
           * we'll wrap it in an SSLException.
           */
          throw (SSLException) new SSLException(msg).initCause(e);
        }
      }
    }
  }
}
  public void runSupport() {

    try {
      new URL(url_str); // determine if this is already a proper URL
    } catch (Throwable t) { // it's not

      //  			//check if the string is just a base32/hex-encoded torrent infohash
      //
      //  		String magnet_uri = UrlUtils.normaliseMagnetURI( url_str );
      //
      //  		if ( magnet_uri != null ){
      //
      //  			url_str = magnet_uri;
      //  		}
    }

    try {
      url = AddressUtils.adjustURL(new URL(url_str));

      String protocol = url.getProtocol().toLowerCase();

      // hack here - the magnet download process requires an additional paramter to cause it to
      // stall on error so the error can be reported

      //    	if ( protocol.equals( "magnet" ) || protocol.equals( "dht" )){
      //
      //    		url = AddressUtils.adjustURL( new URL(url_str+"&pause_on_error=true"));
      //    	}

      for (int i = 0; i < 2; i++) {
        try {

          //	      if ( protocol.equals("https")){
          //
          //	      	// see ConfigurationChecker for SSL client defaults
          //
          //	      	HttpsURLConnection ssl_con = (HttpsURLConnection)url.openConnection();
          //
          //	      	// allow for certs that contain IP addresses rather than dns names
          //
          //	      	ssl_con.setHostnameVerifier(
          //	      			new HostnameVerifier()
          //	      			{
          //	      				public boolean
          //	      				verify(
          //	      					String		host,
          //							SSLSession	session )
          //	      				{
          //	      					return( true );
          //	      				}
          //	      			});
          //
          //	      	con = ssl_con;
          //
          //	      }else{
          //
          con = (HttpURLConnection) url.openConnection();

          //	      }

          con.setRequestProperty(
              "User-Agent", Constants.AZUREUS_NAME + " " + Constants.AZUREUS_VERSION);

          if (referrer != null && referrer.length() > 0) {

            con.setRequestProperty("Referer", referrer);
          }

          if (request_properties != null) {

            Iterator it = request_properties.entrySet().iterator();

            while (it.hasNext()) {

              Map.Entry entry = (Map.Entry) it.next();

              String key = (String) entry.getKey();
              String value = (String) entry.getValue();

              // currently this code doesn't support gzip/deflate...

              if (!key.equalsIgnoreCase("Accept-Encoding")) {

                con.setRequestProperty(key, value);
              }
            }
          }

          this.con.connect();

          break;

          //      	}catch( SSLException e ){
          //
          //      		if ( i == 0 ){
          //
          //      			if ( SESecurityManager.installServerCertificates( url ) != null ){
          //
          //      				// certificate has been installed
          //
          //      				continue;	// retry with new certificate
          //      			}
          //      		}
          //
          //      		throw( e );
          //
        } catch (IOException e) {

          if (i == 0) {

            URL retry_url = UrlUtils.getIPV4Fallback(url);

            if (retry_url != null) {

              url = retry_url;

            } else {

              throw (e);
            }
          }
        }
      }

      int response = this.con.getResponseCode();
      if (!ignoreReponseCode) {
        if ((response != HttpURLConnection.HTTP_ACCEPTED)
            && (response != HttpURLConnection.HTTP_OK)) {
          this.error(response, Integer.toString(response) + ": " + this.con.getResponseMessage());
          return;
        }
      }

      /*
           Map headerFields = this.con.getHeaderFields();

           System.out.println("Header of download of " + url_str);
           for (Iterator iter = headerFields.keySet().iterator(); iter.hasNext();) {
      			String s = (String) iter.next();
      			System.out.println(s + ":" + headerFields.get(s));

      		}
      */
      filename = this.con.getHeaderField("Content-Disposition");
      if ((filename != null)
          && filename
              .toLowerCase()
              .matches(".*attachment.*")) // Some code to handle b0rked servers.
      while (filename.toLowerCase().charAt(0) != 'a') filename = filename.substring(1);
      if ((filename == null)
          || !filename.toLowerCase().startsWith("attachment")
          || (filename.indexOf('=') == -1)) {
        String tmp = this.url.getFile();
        if (tmp.length() == 0 || tmp.equals("/")) {
          filename = url.getHost();
        } else if (tmp.startsWith("?")) {

          // probably a magnet URI - use the hash
          // magnet:?xt=urn:sha1:VGC53ZWCUXUWVGX7LQPVZIYF4L6RXSU6

          String query = tmp.toUpperCase();

          int pos = query.indexOf("XT=URN:SHA1:");

          if (pos == -1) {

            pos = query.indexOf("XT=URN:BTIH:");
          }

          if (pos != -1) {

            pos += 12;

            int p2 = query.indexOf("&", pos);

            if (p2 == -1) {

              filename = query.substring(pos);

            } else {

              filename = query.substring(pos, p2);
            }
          } else {

            filename = "Torrent" + (long) (Math.random() * Long.MAX_VALUE);
          }

          filename += ".tmp";

        } else {
          if (tmp.lastIndexOf('/') != -1) tmp = tmp.substring(tmp.lastIndexOf('/') + 1);

          // remove any params in the url

          int param_pos = tmp.indexOf('?');

          if (param_pos != -1) {
            tmp = tmp.substring(0, param_pos);
          }

          filename = URLDecoder.decode(tmp, Constants.DEFAULT_ENCODING);
        }
      } else {
        filename = filename.substring(filename.indexOf('=') + 1);
        if (filename.startsWith("\"") && filename.endsWith("\""))
          filename = filename.substring(1, filename.lastIndexOf('\"'));

        filename = URLDecoder.decode(filename, Constants.DEFAULT_ENCODING);

        // not sure of this piece of logic here but I'm not changing it at the moment

        File temp = new File(filename);
        filename = temp.getName();
      }

      filename = FileUtil.convertOSSpecificChars(filename, false);

      //      directoryname =
      // COConfigurationManager.getDirectoryParameter("General_sDefaultTorrent_Directory");
      //      boolean useTorrentSave = COConfigurationManager.getBooleanParameter("Save Torrent
      // Files");
      directoryname = "D:\\Development\\testDownloads\\";
      boolean useTorrentSave = true;
      if (file_str != null) {
        // not completely sure about the whole logic in this block
        File temp = new File(file_str);

        // if we're not using a default torrent save dir
        if (!useTorrentSave || directoryname.length() == 0) {
          // if it's already a dir
          if (temp.isDirectory()) {
            // use it
            directoryname = temp.getCanonicalPath();
          }
          // it's a file
          else {
            // so use its parent dir
            directoryname = temp.getCanonicalFile().getParent();
          }
        }

        // if it's a file
        if (!temp.isDirectory()) {
          // set the file name
          filename = temp.getName();
        }
      }
      // what would happen here if directoryname == null and file_str == null??

      this.state = STATE_INIT;
      this.notifyListener();
    } catch (java.net.MalformedURLException e) {
      this.error(0, "Exception while parsing URL '" + url + "':" + e.getMessage());
    } catch (java.net.UnknownHostException e) {
      this.error(
          0,
          "Exception while initializing download of '"
              + url
              + "': Unknown Host '"
              + e.getMessage()
              + "'");
    } catch (java.io.IOException ioe) {
      this.error(0, "I/O Exception while initializing download of '" + url + "':" + ioe.toString());
    } catch (Throwable e) {
      this.error(0, "Exception while initializing download of '" + url + "':" + e.toString());
    }

    if (this.state == STATE_ERROR) {

      return;
    }

    try {
      final boolean status_reader_run[] = {true};

      this.state = STATE_START;

      notifyListener();

      this.state = STATE_DOWNLOADING;

      notifyListener();

      Thread status_reader =
          new AEThread("TorrentDownloader:statusreader") {
            public void runSupport() {
              boolean changed_status = false;

              while (true) {

                try {
                  Thread.sleep(100);

                  try {
                    this_mon.enter();

                    if (!status_reader_run[0]) {

                      break;
                    }
                  } finally {

                    this_mon.exit();
                  }

                  String s = con.getResponseMessage();

                  if (!s.equals(getStatus())) {

                    if (!s.toLowerCase().startsWith("error:")) {

                      if (s.toLowerCase().indexOf("alive") != -1) {

                        if (percentDone < 10) {

                          percentDone++;
                        }
                      }

                      int pos = s.indexOf('%');

                      if (pos != -1) {

                        int i;

                        for (i = pos - 1; i >= 0; i--) {

                          char c = s.charAt(i);

                          if (!Character.isDigit(c) && c != ' ') {

                            i++;

                            break;
                          }
                        }

                        try {
                          percentDone = Integer.parseInt(s.substring(i, pos).trim());

                        } catch (Throwable e) {

                        }
                      }

                      setStatus(s);

                    } else {

                      error(con.getResponseCode(), s.substring(6));
                    }

                    changed_status = true;
                  }

                } catch (Throwable e) {

                  break;
                }
              }

              if (changed_status) {

                setStatus("");
              }
            }
          };

      status_reader.setDaemon(true);

      status_reader.start();

      InputStream in;

      try {
        in = this.con.getInputStream();

      } catch (FileNotFoundException e) {
        if (ignoreReponseCode) {

          in = this.con.getErrorStream();
        } else {

          throw e;
        }

      } finally {

        try {
          this_mon.enter();

          status_reader_run[0] = false;

        } finally {

          this_mon.exit();
        }
      }

      // handle some servers that return gzip'd torrents even though we don't request it!

      String encoding = con.getHeaderField("content-encoding");

      if (encoding != null) {

        if (encoding.equalsIgnoreCase("gzip")) {

          in = new GZIPInputStream(in);

        } else if (encoding.equalsIgnoreCase("deflate")) {

          in = new InflaterInputStream(in);
        }
      }

      if (this.state != STATE_ERROR) {

        this.file = new File(this.directoryname, filename);

        boolean useTempFile = false;
        try {
          this.file.createNewFile();
          useTempFile = !this.file.exists();
        } catch (Throwable t) {
          useTempFile = true;
        }

        if (useTempFile) {
          this.file = File.createTempFile("AZU", ".torrent", new File(this.directoryname));
          this.file.createNewFile();
        }

        FileOutputStream fileout = new FileOutputStream(this.file, false);

        bufBytes = 0;

        int size = (int) UrlUtils.getContentLength(con);

        this.percentDone = -1;

        do {
          if (this.cancel) {
            break;
          }

          try {
            bufBytes = in.read(buf);

            this.readTotal += bufBytes;

            if (size != 0) {
              this.percentDone = (100 * this.readTotal) / size;
            }

            notifyListener();

          } catch (IOException e) {
          }

          if (bufBytes > 0) {
            fileout.write(buf, 0, bufBytes);
          }
        } while (bufBytes > 0);

        in.close();

        fileout.flush();

        fileout.close();

        if (this.cancel) {
          this.state = STATE_CANCELLED;
          if (deleteFileOnCancel) {
            this.cleanUpFile();
          }
        } else {
          if (this.readTotal <= 0) {
            this.error(0, "No data contained in '" + this.url.toString() + "'");
            return;
          }

          // if the file has come down with a not-so-useful name then we try to rename
          // it to something more useful

          try {
            if (!filename.toLowerCase().endsWith(".torrent")) {

              TOTorrent torrent = TorrentUtils.readFromFile(file, false);

              String name = TorrentUtils.getLocalisedName(torrent) + ".torrent";

              File new_file = new File(directoryname, name);

              if (file.renameTo(new_file)) {

                filename = name;

                file = new_file;
              }
            }
          } catch (Throwable e) {

            Debug.printStackTrace(e);
          }

          //	          TorrentUtils.setObtainedFrom( file, original_url );

          this.state = STATE_FINISHED;
        }
        this.notifyListener();
      }
    } catch (Exception e) {

      if (!cancel) {

        Debug.out("'" + this.directoryname + "' '" + filename + "'", e);
      }

      this.error(0, "Exception while downloading '" + this.url.toString() + "':" + e.getMessage());
    }
  }
  public Socket createSocket(String host, int port)
      throws IOException, FingerprintException, FingerprintError {

    Socket s = new Socket();
    s.connect(new InetSocketAddress(proxyHost, proxyPort), 1000 * timeout);

    PrintWriter out = new PrintWriter(s.getOutputStream());
    InputStreamReader isr = new InputStreamReader(s.getInputStream());
    BufferedReader in = new BufferedReader(isr);

    String[] connHdr = {
      "CONNECT " + host + ":" + port + " HTTP/1.1",
      "Host: " + host + ":" + port,
      "Proxy-Connection: close"
    };

    String[] connReq = null;

    if (authRequired) {
      connReq = new String[connHdr.length + 1];
      for (int i = 0; i < connHdr.length; i++) {
        connReq[i] = connHdr[i];
      }
      connReq[connHdr.length] = "Proxy-Authorization: " + authenticator;
    } else {
      connReq = new String[connHdr.length];
      for (int i = 0; i < connHdr.length; i++) {
        connReq[i] = connHdr[i];
      }
    }
    sendRequest(out, connReq);

    HttpResponseHeader hdr = new HttpResponseHeader(in);
    int status = hdr.getStatusCode();

    if (Debug.get(Debug.Communication)) {
      System.err.println("Got status " + status);
    }

    /* Now check the status code and act upon it */
    if ((status / 100) == 2) { // Code is 2xx
      return s;
    }

    if (((status / 100) == 4) && (status != 407)) {
      throw new HttpProxyError("Proxy request failed");
    }

    if ((status == 407) && authRequired) {
      /* We already sent authorisation info,
       * but the proxy did not accept it.
       * So we can only stop here, as
       * we do not have the right credentials
       * at hand.
       */
      throw new HttpProxyError("Cannot authenticate " + "to proxy, wrong " + "credentials?");
    }
    if (status == 407) {
      if (Debug.get(Debug.SockInit)) {
        System.err.println("Preparing proxy " + "authenticator for " + hdr.getProxyAuthInfo());
      }
      prepareAuthorisation(hdr.getProxyAuthInfo());
      authRequired = true;
      return createSocket(host, port);
    }
    return s;
  }
Esempio n. 13
-1
  /*
   * Calculate the keys needed for this connection, once the session's
   * master secret has been calculated.  Uses the master key and nonces;
   * the amount of keying material generated is a function of the cipher
   * suite that's been negotiated.
   *
   * This gets called both on the "full handshake" (where we exchanged
   * a premaster secret and started a new session) as well as on the
   * "fast handshake" (where we just resumed a pre-existing session).
   */
  void calculateConnectionKeys(SecretKey masterKey) {
    /*
     * For both the read and write sides of the protocol, we use the
     * master to generate MAC secrets and cipher keying material.  Block
     * ciphers need initialization vectors, which we also generate.
     *
     * First we figure out how much keying material is needed.
     */
    int hashSize = cipherSuite.macAlg.size;
    boolean is_exportable = cipherSuite.exportable;
    BulkCipher cipher = cipherSuite.cipher;
    int expandedKeySize = is_exportable ? cipher.expandedKeySize : 0;

    // Which algs/params do we need to use?
    String keyMaterialAlg;
    PRF prf;

    if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
      keyMaterialAlg = "SunTls12KeyMaterial";
      prf = cipherSuite.prfAlg;
    } else {
      keyMaterialAlg = "SunTlsKeyMaterial";
      prf = P_NONE;
    }

    String prfHashAlg = prf.getPRFHashAlg();
    int prfHashLength = prf.getPRFHashLength();
    int prfBlockSize = prf.getPRFBlockSize();

    TlsKeyMaterialParameterSpec spec =
        new TlsKeyMaterialParameterSpec(
            masterKey,
            protocolVersion.major,
            protocolVersion.minor,
            clnt_random.random_bytes,
            svr_random.random_bytes,
            cipher.algorithm,
            cipher.keySize,
            expandedKeySize,
            cipher.ivSize,
            hashSize,
            prfHashAlg,
            prfHashLength,
            prfBlockSize);

    try {
      KeyGenerator kg = JsseJce.getKeyGenerator(keyMaterialAlg);
      kg.init(spec);
      TlsKeyMaterialSpec keySpec = (TlsKeyMaterialSpec) kg.generateKey();

      clntWriteKey = keySpec.getClientCipherKey();
      svrWriteKey = keySpec.getServerCipherKey();

      // Return null if IVs are not supposed to be generated.
      // e.g. TLS 1.1+.
      clntWriteIV = keySpec.getClientIv();
      svrWriteIV = keySpec.getServerIv();

      clntMacSecret = keySpec.getClientMacKey();
      svrMacSecret = keySpec.getServerMacKey();
    } catch (GeneralSecurityException e) {
      throw new ProviderException(e);
    }

    //
    // Dump the connection keys as they're generated.
    //
    if (debug != null && Debug.isOn("keygen")) {
      synchronized (System.out) {
        HexDumpEncoder dump = new HexDumpEncoder();

        System.out.println("CONNECTION KEYGEN:");

        // Inputs:
        System.out.println("Client Nonce:");
        printHex(dump, clnt_random.random_bytes);
        System.out.println("Server Nonce:");
        printHex(dump, svr_random.random_bytes);
        System.out.println("Master Secret:");
        printHex(dump, masterKey.getEncoded());

        // Outputs:
        System.out.println("Client MAC write Secret:");
        printHex(dump, clntMacSecret.getEncoded());
        System.out.println("Server MAC write Secret:");
        printHex(dump, svrMacSecret.getEncoded());

        if (clntWriteKey != null) {
          System.out.println("Client write key:");
          printHex(dump, clntWriteKey.getEncoded());
          System.out.println("Server write key:");
          printHex(dump, svrWriteKey.getEncoded());
        } else {
          System.out.println("... no encryption keys used");
        }

        if (clntWriteIV != null) {
          System.out.println("Client write IV:");
          printHex(dump, clntWriteIV.getIV());
          System.out.println("Server write IV:");
          printHex(dump, svrWriteIV.getIV());
        } else {
          if (protocolVersion.v >= ProtocolVersion.TLS11.v) {
            System.out.println("... no IV derived for this protocol");
          } else {
            System.out.println("... no IV used for this cipher");
          }
        }
        System.out.flush();
      }
    }
  }
Esempio n. 14
-5
  /*
   * Calculate the master secret from its various components.  This is
   * used for key exchange by all cipher suites.
   *
   * The master secret is the catenation of three MD5 hashes, each
   * consisting of the pre-master secret and a SHA1 hash.  Those three
   * SHA1 hashes are of (different) constant strings, the pre-master
   * secret, and the nonces provided by the client and the server.
   */
  private SecretKey calculateMasterSecret(
      SecretKey preMasterSecret, ProtocolVersion requestedVersion) {

    if (debug != null && Debug.isOn("keygen")) {
      HexDumpEncoder dump = new HexDumpEncoder();

      System.out.println("SESSION KEYGEN:");

      System.out.println("PreMaster Secret:");
      printHex(dump, preMasterSecret.getEncoded());

      // Nonces are dumped with connection keygen, no
      // benefit to doing it twice
    }

    // What algs/params do we need to use?
    String masterAlg;
    PRF prf;

    if (protocolVersion.v >= ProtocolVersion.TLS12.v) {
      masterAlg = "SunTls12MasterSecret";
      prf = cipherSuite.prfAlg;
    } else {
      masterAlg = "SunTlsMasterSecret";
      prf = P_NONE;
    }

    String prfHashAlg = prf.getPRFHashAlg();
    int prfHashLength = prf.getPRFHashLength();
    int prfBlockSize = prf.getPRFBlockSize();

    TlsMasterSecretParameterSpec spec =
        new TlsMasterSecretParameterSpec(
            preMasterSecret,
            protocolVersion.major,
            protocolVersion.minor,
            clnt_random.random_bytes,
            svr_random.random_bytes,
            prfHashAlg,
            prfHashLength,
            prfBlockSize);

    SecretKey masterSecret;
    try {
      KeyGenerator kg = JsseJce.getKeyGenerator(masterAlg);
      kg.init(spec);
      masterSecret = kg.generateKey();
    } catch (GeneralSecurityException e) {
      // For RSA premaster secrets, do not signal a protocol error
      // due to the Bleichenbacher attack. See comments further down.
      if (!preMasterSecret.getAlgorithm().equals("TlsRsaPremasterSecret")) {
        throw new ProviderException(e);
      }

      if (debug != null && Debug.isOn("handshake")) {
        System.out.println("RSA master secret generation error:");
        e.printStackTrace(System.out);
        System.out.println("Generating new random premaster secret");
      }

      if (requestedVersion != null) {
        preMasterSecret = RSAClientKeyExchange.generateDummySecret(requestedVersion);
      } else {
        preMasterSecret = RSAClientKeyExchange.generateDummySecret(protocolVersion);
      }

      // recursive call with new premaster secret
      return calculateMasterSecret(preMasterSecret, null);
    }

    // if no version check requested (client side handshake), or version
    // information is not available (not an RSA premaster secret),
    // return master secret immediately.
    if ((requestedVersion == null) || !(masterSecret instanceof TlsMasterSecret)) {
      return masterSecret;
    }

    // we have checked the ClientKeyExchange message when reading TLS
    // record, the following check is necessary to ensure that
    // JCE provider does not ignore the checking, or the previous
    // checking process bypassed the premaster secret version checking.
    TlsMasterSecret tlsKey = (TlsMasterSecret) masterSecret;
    int major = tlsKey.getMajorVersion();
    int minor = tlsKey.getMinorVersion();
    if ((major < 0) || (minor < 0)) {
      return masterSecret;
    }

    // check if the premaster secret version is ok
    // the specification says that it must be the maximum version supported
    // by the client from its ClientHello message. However, many
    // implementations send the negotiated version, so accept both
    // for SSL v3.0 and TLS v1.0.
    // NOTE that we may be comparing two unsupported version numbers, which
    // is why we cannot use object reference equality in this special case.
    ProtocolVersion premasterVersion = ProtocolVersion.valueOf(major, minor);
    boolean versionMismatch = (premasterVersion.v != requestedVersion.v);

    /*
     * we never checked the client_version in server side
     * for TLS v1.0 and SSL v3.0. For compatibility, we
     * maintain this behavior.
     */
    if (versionMismatch && requestedVersion.v <= ProtocolVersion.TLS10.v) {
      versionMismatch = (premasterVersion.v != protocolVersion.v);
    }

    if (versionMismatch == false) {
      // check passed, return key
      return masterSecret;
    }

    // Due to the Bleichenbacher attack, do not signal a protocol error.
    // Generate a random premaster secret and continue with the handshake,
    // which will fail when verifying the finished messages.
    // For more information, see comments in PreMasterSecret.
    if (debug != null && Debug.isOn("handshake")) {
      System.out.println(
          "RSA PreMasterSecret version error: expected"
              + protocolVersion
              + " or "
              + requestedVersion
              + ", decrypted: "
              + premasterVersion);
      System.out.println("Generating new random premaster secret");
    }
    preMasterSecret = RSAClientKeyExchange.generateDummySecret(requestedVersion);

    // recursive call with new premaster secret
    return calculateMasterSecret(preMasterSecret, null);
  }