public void run() {
    try {
      state.setValue(TransportProtocolState.NEGOTIATING_PROTOCOL);
      log.info("Registering transport protocol messages with inputstream");
      algorithmsOut = new TransportProtocolAlgorithmSync();
      algorithmsIn = new TransportProtocolAlgorithmSync();

      // Create the input/output streams
      sshIn = new TransportProtocolInputStream(this, provider.getInputStream(), algorithmsIn);
      sshOut = new TransportProtocolOutputStream(provider.getOutputStream(), this, algorithmsOut);

      // Register the transport layer messages that this class will handle
      messageStore.registerMessage(SshMsgDisconnect.SSH_MSG_DISCONNECT, SshMsgDisconnect.class);
      messageStore.registerMessage(SshMsgIgnore.SSH_MSG_IGNORE, SshMsgIgnore.class);
      messageStore.registerMessage(
          SshMsgUnimplemented.SSH_MSG_UNIMPLEMENTED, SshMsgUnimplemented.class);
      messageStore.registerMessage(SshMsgDebug.SSH_MSG_DEBUG, SshMsgDebug.class);
      messageStore.registerMessage(SshMsgKexInit.SSH_MSG_KEX_INIT, SshMsgKexInit.class);
      messageStore.registerMessage(SshMsgNewKeys.SSH_MSG_NEWKEYS, SshMsgNewKeys.class);
      registerTransportMessages();

      List list = SshKeyExchangeFactory.getSupportedKeyExchanges();
      Iterator it = list.iterator();

      while (it.hasNext()) {
        String keyExchange = (String) it.next();
        SshKeyExchange kex = SshKeyExchangeFactory.newInstance(keyExchange);
        kex.init(this);
        kexs.put(keyExchange, kex);
      }

      // call abstract to initialise the local ident string
      setLocalIdent();

      // negotiate the protocol version
      negotiateVersion();
      startBinaryPacketProtocol();
    } catch (Throwable e) {
      if (e instanceof IOException) {
        state.setLastError((IOException) e);
      }

      if (state.getValue() != TransportProtocolState.DISCONNECTED) {
        log.error("The Transport Protocol thread failed", e);

        // log.info(e.getMessage());
        stop();
      }
    } finally {
      thread = null;
    }

    log.debug("The Transport Protocol has been stopped");
  }
  /**
   * @throws IOException
   * @throws KeyExchangeException
   */
  protected void beginKeyExchange() throws IOException, KeyExchangeException {
    log.info("Starting key exchange");

    // state.setValue(TransportProtocolState.PERFORMING_KEYEXCHANGE);
    String kexAlgorithm = "";

    // We now have both kex inits, this is where client/server
    // implemtations take over so call abstract methods
    try {
      // Determine the key exchange algorithm
      kexAlgorithm = getKexAlgorithm();

      if (log.isDebugEnabled()) {
        log.debug("Key exchange algorithm: " + kexAlgorithm);
      }

      // Get an instance of the key exchange algortihm
      SshKeyExchange kex = (SshKeyExchange) kexs.get(kexAlgorithm);

      // Do the key exchange
      performKeyExchange(kex);

      // Record the output
      exchangeHash = kex.getExchangeHash();

      if (sessionIdentifier == null) {
        sessionIdentifier = new byte[exchangeHash.length];
        System.arraycopy(exchangeHash, 0, sessionIdentifier, 0, sessionIdentifier.length);
        thread.setSessionId(sessionIdentifier);
      }

      hostKey = kex.getHostKey();
      signature = kex.getSignature();
      k = kex.getSecret();

      // Send new keys
      sendNewKeys();
      kex.reset();
    } catch (AlgorithmNotAgreedException e) {
      sendDisconnect(
          SshMsgDisconnect.KEY_EXCHANGE_FAILED, "No suitable key exchange algorithm was agreed");
      throw new KeyExchangeException("No suitable key exchange algorithm could be agreed.");
    }
  }