ssh远程连接1.5版本,java

第一次写文章,有点小紧张。下面进入正题。

最近有需求ssh远程连接,服务器是1.5版本的,在网上百度很多都是2.0版本。找的到的1.5版本基本都是英文网站(英语渣的我一头雾水),很多还是404根本点不开。更加严重的是java里有个ssh框架,惨啊,关键词搜索出来的还大多是这么一个东西。

然后呢,终于让我找到了这个网址http://www.pitman.co.za/projects/jssh/,进去里边左侧有Download,点进去,下载jssh-0.9.5.tar.gz解压就行了。解压后的文件大都是后缀为java的文件,我是要在我的项目里添加ssh1.5的功能,如果把这些文件都复制粘贴进来就太麻烦了,而且也显得很乱。所以我就将他们一起生成了一个jssh.jar,然后导入我的java项目中。最后我模仿解压文件里的SSHClient.java写了一个SSH1_5Client,下边是代码。

---------------------------分割线----------------------------

我回来更新了,之前我写SSH1_5Client的方法根本就行不通,只能执行一条语句,不能执行连续的指令。而且原作者写的也是一种交互式的程序,即在控制台中启动SSHClient.java的main方法后,可以说整个控制台就变成了ssh的客户端,由客户实时输入指令,控制台实时输出结果。而且在jssh代码中搜索我发现有System.exit语句。这个出现的本意是程序出现异常后就停止,但是我是要整合近我的项目中去的,如果一有异常就停止程序那就糟糕了。

之前贴的代码我删了,以下先贴上我最新改进的代码,替换原文件的两个文件,经过多轮测试后没发现有问题,如果有人找到问题欢迎提出,之后有空我也会回来详细说一下我的代码。

---------------------------分割线----------------------------

我回来了,一开始我想我还是稍微简单说明一下原作者的程序是怎么工作的吧。

1.首先是启动程序,程序的入口就是SSHClient的main方法。

2.接着需要用户键入密码,此时程序中存在以下五个相关的线程:

TimeoutThread:程序启动后立刻生成,设定为等待60秒,60秒后如果ssh未连接成功,则System.exit结束程序。

ReaderThread:程序启动后立刻生成,监听STDOUT_InputStream(封装的标准输出流),在控制台System.out输出所有结果。

WriterThread:连接成功后生成,从控制台System.in接收所有用户输入,发送到STDIN_OutputStream(封装的标准输入流)

KeepaliveThread:连接成功后生成(需要设置超时时间),定时循环发送MSG_IGNORE(封装的信息包)到PacketQueue(封装的信息包队列)。

ClientProtocolHandler:连接成功后生成,监听PacketQueue(封装的信息包队列),队列不空则发送信息包到SSHOutputStream(这个流才是真正的与远程进行信息交互的流,根据Socket生成的流封装),出现异常System.exit结束程序。

发送一次指令时的数据流向:

控制台输入--->STDIN_OutputStream--->PacketQueue--->SSHOutputStream--->Socket

                             控制台输出<---STDOUT_InputStream<---SSHInputStream<---Socket

---------------------------分割线----------------------------

下边就谈一下我是怎么改的:

1.System.exit出现的地方都改为抛出异常。

2.把ReaderThread、WriterThread俩线程去掉,输入输出当然不能再依赖控制台了。

3.KeepaliveThread小修改。

4.去掉TimeoutThread类,增加同名TimeoutThread类实现超时功能。

5.ClientProtocolHandler不再实现Runnable接口,也就是说去掉了ClientProtocolHandler线程,类保留了,并且大改造。


1、2点好改,删点代码加点代码就搞掂,第3点改成能结束的循环,然后第4点就利用Callable来实现,代码里有注释,我就不多说了。

第5点删掉线程,相当于删掉了PacketQueue的监听器。这样输入的命令永远停在PacketQueue里边,发不到远程那里。

所以我删掉了ClientProtocolHandler类中这一行。

private PacketQueue _clientqueue = new PacketQueue();

涉及到PacketQueue的入队的代码改成直接发送到SSHOutputStream,然后设计到多个命令的执行的execShell也做了修改。

总之改完后就是如下俩个类。

/* class SSHClient
 *
 * Copyright (C) 2002  R M Pitman <http://www.pitman.co.za>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

package jssh;

import java.net.*;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.io.*;
import java.math.BigInteger;
import de.mud.ssh.Cipher;

public class SSHClient {
	/**
	 * Constructor
	 */
	public SSHClient(Options options_) {
		_options = options_;
	}

	private class TimeoutThread<T> implements Callable<Exception> {
		final ClientProtocolHandler _handler;

		@Override
		public Exception call() {
			try {
				_handler.exchangeIdStrings();
			} catch (Exception e) {
				e.printStackTrace();
				return e;
			}
			return null;
		}

		TimeoutThread(ClientProtocolHandler _handler) {
			this._handler = _handler;
		}
	}

	private Exception connect(long timeout) {
		ExecutorService exec = Executors.newFixedThreadPool(1);
		Future<Exception> future = exec.submit(new TimeoutThread<>(_handler));
		Exception exception = null;
		try {
			exception = future.get(timeout, TimeUnit.MILLISECONDS);
		} catch (Exception e) {// get方法超时后会立刻抛出e.getMessage()=null的异常
			exception = new Exception("  timeout in authentication.");
		} finally {
			if (future != null && !future.isCancelled()) {
				future.cancel(true);
			}
			exec.shutdownNow();
		}
		return exception;
	}

	/**
	 * This method connects to the server and starts up the protocol handler.
	 */
	public void connect(String password) throws Exception {
		/*
		 * Resolve the server address.
		 */
		InetAddress serverAddress = null;
		String servername = _options.getHostname();
		try {
			serverAddress = InetAddress.getByName(servername);
		} catch (UnknownHostException e) {
			throw new Exception("  Unknown host " + servername);
		}

		System.out.print("  Connecting to " + servername + "...");
		Socket socket = new Socket(serverAddress, _options.getPort());

		System.out.println("  Connected");

		/*
		 * Create an SSH protocol handler.
		 */
		_handler = new ClientProtocolHandler(socket, _options);

		/**
		 * 超时强制退出,可以看一下源码里ClientProtocolHandler类的exchangeIdStrings方法,这个方法里有这么一句_instream.read();
		 * 这是socket连接后读取的第一个字符,但是有时候永远不会有东西读到,我使用来测试的机器大概20次就出现一次这种情况,那么这就永远阻塞在这里了。
		 * 所以我写了一个超时方法。
		 * 此外,下边还提供了close()对socket进行关闭,因为即使程序可以执行下去了,但是在connect(20000)方法里创建的线程仍然不屈不挠的在等那个缺席的字符。
		 * 虽然方法里有cancel()和shutdownNow()来结束线程,但是这俩方法只能停止非阻塞线程。
		 * 所以下边强制关闭socket,而在socket强制关闭后,那个阻塞的_instream.read();就会报Socketclosed的错误,然后才能结束。
		 * 至于Socketclosed的报错就不是我要关注的点了,可以忽视掉。
		 */
		Exception exception = connect(20000);
		if (exception != null) {
			throw exception;
		}

		_handler.receiveServerKey();

		/*
		 * Check that the server's host key is in the known_hosts file (but skip the
		 * check if we are connecting to localhost).
		 */
		String server = socket.getInetAddress().getHostAddress();
		if (server.equals("127.0.0.1") == false) {
			check_host_key(_handler.getServerKeyPacket(), servername);
		} else
			debug("Forcing acceptance of host key for localhost");

		/*
		 * Instantiate an object that reads true random bits from the /dev/urandom
		 * device. This works on Linux and any other OS that has /dev/random or
		 * /dev/urandom; on other OS's, such as Windoze, you'll have to supply your own
		 * object that implements the TrueRandom interface. Note that it is preferable
		 * to use /dev/random, but /dev/random blocks if it doesn't have the requested
		 * number of bits in its entropy pool, so it can cause unacceptably long delays.
		 */
		ITrueRandom trueRandom = new DevURandom();
		_handler.sendSessionKey(trueRandom);

		String userName = _options.getUser();

		boolean authRequired = _handler.declareUser(userName);
		if (authRequired) {
			RSAPrivateKeyFile keyfile = null;
			try {
				keyfile = new RSAPrivateKeyFile(_options.getIdentityFile());
			} catch (FileNotFoundException e) {
			}

			// If the private-key file exists, try RSA authentication
			// first.
			if (keyfile != null) {
				RSAPrivateKey privateKey = null;
				if (keyfile.getCipherType() != Cipher.SSH_CIPHER_NONE) {
					privateKey = keyfile.getPrivateKey(RSAKeyPassphrase);
				} else {
					privateKey = keyfile.getPrivateKey();
				}

				if (_handler.authenticateUser(privateKey) == true) {
					authRequired = false;
				}
			} else {
				_handler.debug("unknown identity file " + _options.getIdentityFile());
			}

			// Either the private-key file doesn't exist, or RSA
			// authentication failed.
			if (authRequired) {
				if (!_handler.authenticateUser(userName, password))
					throw new SSHAuthFailedException();
			}
		}

		/*
		 * Request compression, port-forwarding etc.
		 */
		_handler.preparatoryOperations();

		return;
	}

	public String execCmd() throws IOException, SSHProtocolException {
		String command = _options.getCommand();
		if (command == null) {
			return "";
		}
		_handler.execCmd(command);
		_handler.getSTDOUT().enqueue(new SMSG_STDERR_DATA("\ndny_execCmd\n"));// 添加结束标志
		StringBuffer sb = new StringBuffer();
		String s;
		try (BufferedReader reader = new BufferedReader(new InputStreamReader(_handler.getSTDOUT()));) {
			while ((s = reader.readLine()) != null) {
				if (s.equals("dny_execCmd")) {
					break;
				}
				sb.append(s + "\n");
			}
		}
		return sb.toString().trim();
	}

	public String execShell(String[] commands, String[] end) throws IOException, SSHProtocolException {
		if (_options.getCommand() != null) {
			return "";
		}
		_handler.execShell(commands, end);
		_handler.getSTDOUT().enqueue(new SMSG_STDERR_DATA("\ndny_execShell\n"));// 添加结束标志
		StringBuffer sb = new StringBuffer();
		String s;
		try (BufferedReader reader = new BufferedReader(new InputStreamReader(_handler.getSTDOUT()));) {
			while ((s = reader.readLine()) != null) {
				if (s.equals("dny_execShell")) {
					break;
				}
				sb.append(s + "\n");
			}
		}
		return sb.toString().trim();
	}

	public void close() {
		if (_handler != null) {
			try {
				_handler.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}

	/**
	 * Check if the received server key is in the known_hosts file.
	 */
	private void check_host_key(SMSG_PUBLIC_KEY packet_, String servername_) throws IOException, SSHSetupException {
		BigInteger host_key_public_modulus = new BigInteger(1, packet_.getHostKeyPublicModulus());
		BigInteger host_key_public_exponent = new BigInteger(1, packet_.getHostKeyPublicExponent());

		// First check in the global known_hosts file.
		KnownHostsFile globalKnownHosts = new KnownHostsFile("/etc/ssh/ssh_known_hosts");

		int status = globalKnownHosts.check_host_key(servername_, host_key_public_modulus, host_key_public_exponent);

		String user_known_hosts = System.getProperty("user.home") + "/.ssh/known_hosts";
		KnownHostsFile userKnownHosts = new KnownHostsFile(user_known_hosts);

		if (status == KnownHostsFile.HOST_KEY_NEW) {
			// It wasn't there, so check the user known_hosts.
			status = userKnownHosts.check_host_key(servername_, host_key_public_modulus, host_key_public_exponent);
		}

		/*
		 * If the server's host key was not found in either of the known_hosts files,
		 * add it to the user's known_hosts.
		 */
		if (status == KnownHostsFile.HOST_KEY_NEW) {
			debug("Adding host key to " + user_known_hosts);
			userKnownHosts.add_host_key(servername_, host_key_public_modulus, host_key_public_exponent);
		} else if (status == KnownHostsFile.HOST_KEY_DIFFERS) {
			throw new SSHSetupException("Server host key changed!");
		}
	}

	private void debug(String string_) {
		if (_options.getDebug())
			System.err.println("debug: " + string_);
	}

	// ====================================================================
	// INSTANCE VARIABLES

	private Options _options;

	private ClientProtocolHandler _handler;

	private String RSAKeyPassphrase;
}
package jssh;

import java.io.*;
import java.net.*;
import java.util.*;
import java.security.*;
import de.mud.ssh.Cipher;

/**
 * This class performs all the SSH client protocol handling on a specified
 * network connection.
 * <p>
 *
 * An example of how to use this protocol handler is provided in the
 * {@link SSHClient SSHClient} class.
 */
public class ClientProtocolHandler implements IProtocolHandler, IPacketConstants {
	public ClientProtocolHandler(Socket socket_, Options options_) throws IOException {
		_socket = socket_;
		_instream = new BufferedInputStream(socket_.getInputStream());
		_outstream = new BufferedOutputStream(socket_.getOutputStream());
		_options = options_;
	}

	/**
	 * Enqueues a packet to the SSH server.
	 */
	public void enqueueToRemote(Packet packet_) {
		try {
			_ssh_out.writePacket(packet_);
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	/**
	 * Enqueues a packet to the STDOUT.
	 */
	public void enqueueToStdout(Packet packet_) {
		_stdout.enqueue(packet_);
	}

	/**
	 * Returns a reference to the InputStream object from which data must be read
	 * (for displaying on the screen).
	 */
	public STDOUT_InputStream getSTDOUT() {
		return _stdout;
	}

	/**
	 * Returns a reference to the OutputStream object to which keyboard data must be
	 * written (for sending to the server).
	 */
	public STDIN_OutputStream getSTDIN() {
		if (_stdin == null)
			_stdin = new STDIN_OutputStream(this);
		return _stdin;
	}

	/**
	 * This method exchanges version identification strings with the SSH server.
	 * 
	 * @exception SSHSetupException if the server's version string is incompatible.
	 */
	public void exchangeIdStrings() throws IOException, SSHSetupException {
		if (_phase != PHASE_EXCHANGE_ID_STRINGS) {
			throw new RuntimeException("SSH protocol already in packet mode");
		}

		for (;;) {
			int b = _instream.read();
			if (b == '\n') {
				debug("Remote version: " + _id_string_received.toString());
				debug("Local version string: " + ID_STRING_SENT);

				_outstream.write((ID_STRING_SENT + "\n").getBytes());
				_outstream.flush();

				// Here we should check that the server version is compatible.
				if (_id_string_received.toString().startsWith("SSH-1") == false) {
					throw new SSHSetupException("incompatible server version: " + _id_string_received.toString());
				}
				_phase = PHASE_RECEIVE_SERVER_KEY;
				return;
			}
			_id_string_received.append((char) b);
		}
	}

	/**
	 * Wait for the SSH_SMSG_PUBLIC_KEY packet from the server. This method must be
	 * called after <code>exchangeIdStrings()</code> has returned successfully.
	 */
	public void receiveServerKey() throws IOException, SSHSetupException, SSHProtocolException {
		if (_phase != PHASE_RECEIVE_SERVER_KEY) {
			throw new RuntimeException("SSH protocol not in packet mode yet");
		}

		_ssh_in = new SSHInputStream(_instream);
		_ssh_out = new SSHOutputStream(_outstream);

		debug("Waiting for server public key.");
		Packet packet = readNextPacket();
		if (packet.getPacketType() != SSH_SMSG_PUBLIC_KEY) {
			throw new SSHProtocolException(
					"Received packet type " + packet.getPacketType() + ", expected SSH_SMSG_PUBLIC_KEY");
		}
		_server_public_key_packet = (SMSG_PUBLIC_KEY) packet;
		debug("Received server public key (" + (8 * _server_public_key_packet.getServerKeyPublicModulus().length)
				+ " bits) and host key (" + (8 * _server_public_key_packet.getHostKeyPublicModulus().length)
				+ " bits).");

		_phase = PHASE_SEND_SESSION_KEY;
		return;
	}

	/**
	 * Create and send an encrypted session key; this method must be called AFTER
	 * receiveServerKey() has returned successfully.
	 *
	 * @param trueRandom_ an object that implements the ITrueRandom interface and
	 *                    supplies truly random bits for the session key. Note that
	 *                    it is <strong>not</strong> sufficient to use a
	 *                    pseudo-random number generator (PRNG) seeded by the system
	 *                    clock; for an explanation, see
	 *                    <a href="http://www.ietf.org/rfc/rfc1750.txt">rfc1750
	 *                    (Randomness Recommendations for Security)</a>
	 *                    <p>
	 *
	 *                    On a Linux system (or any system that provides the
	 *                    /dev/random device), you can use the DevRandom class,
	 *                    which implements the ITrueRandom interface. On other
	 *                    operating systems you will have to provide your own source
	 *                    of true random bits. See
	 *                    <a href="http://www.cs.berkeley.edu/~daw/rnd/">Randomness
	 *                    for Crypto</a> for information on generating sufficiently
	 *                    random numbers.
	 */
	public void sendSessionKey(ITrueRandom trueRandom_) throws SSHProtocolException, SSHSetupException, IOException {
		if (_phase != PHASE_SEND_SESSION_KEY) {
			throw new RuntimeException("SSH protocol not in valid state for sending session key");
		}

		/*
		 * Send an SSH_CMSG_SESSION_KEY message in reply.
		 */
		CMSG_SESSION_KEY session_key_packet = new CMSG_SESSION_KEY(trueRandom_, _server_public_key_packet);
		_session_id = session_key_packet.getSessionID(); // save it.
		_ssh_out.writePacket(session_key_packet);
		debug("Sent encrypted session key.");

		/*
		 * Now we have exchanged session keys, enable encryption.
		 */
		String cipherName = session_key_packet.getCipherName();
		Cipher sendCipher = Cipher.getInstance(cipherName);
		if (sendCipher == null) {
			throw new SSHSetupException(cipherName + " algorithm not supported");
		}
		debug("Encryption type: " + cipherName);
		sendCipher.setKey(session_key_packet.getSessionKey());
		_ssh_out.setCipher(sendCipher);

		Cipher receiveCipher = Cipher.getInstance(cipherName);
		receiveCipher.setKey(session_key_packet.getSessionKey());
		_ssh_in.setCipher(receiveCipher);

		Packet packet = readNextPacket();
		if (packet.getPacketType() != SSH_SMSG_SUCCESS) {
			throw new SSHProtocolException(
					"Received packet type " + packet.getPacketType() + ", expected SSH_SMSG_SUCCESS");
		}
		debug("Received encrypted confirmation.");

		_phase = PHASE_DECLARE_USER;
	}

	/**
	 * Declares the username to the server. This method must be called AFTER
	 * sendSessionKey() has returned successfully.
	 * 
	 * @return false if the server is willing to accept the user without further
	 *         authentication; true if authentication is required.
	 */
	public boolean declareUser(String username_) throws IOException, SSHProtocolException {
		if (_phase != PHASE_DECLARE_USER) {
			throw new RuntimeException("SSH protocol in invalid state for declaring user");
		}
		CMSG_USER user_packet = new CMSG_USER(username_);
		_ssh_out.writePacket(user_packet);

		Packet packet = readNextPacket();
		if (packet.getPacketType() == SSH_SMSG_SUCCESS) {
			// The server has accepted our login; a password is not required.
			_phase = PHASE_PREPARATORY;
			return false;
		}

		if (packet.getPacketType() != SSH_SMSG_FAILURE) {
			throw new SSHProtocolException("Received packet type " + packet.getPacketType()
					+ ", expected SSH_SMSG_SUCCESS or SSH_SMSG_FAILURE");
		}
		_phase = PHASE_AUTHENTICATE;
		return true;
	}

	/**
	 * Authenticate the user using username-password authentication. This method
	 * must be called after declareUser() has returned successfully with a return
	 * value of <code>true</code>.
	 * 
	 * @param user_     the username.
	 * @param password_ the password string.
	 * @return true if the server accepted the username and password.
	 * @exception IOException          if a network error occurred.
	 * @exception SSHProtocolException if a malformed/invalid SSH packet was
	 *                                 received.
	 */
	public boolean authenticateUser(String user_, String password_) throws IOException, SSHProtocolException {
		if (_phase != PHASE_AUTHENTICATE) {
			throw new RuntimeException("SSH protocol in invalid state for user authentication");
		}

		/*
		 * The server has sent us an encrypted packet and we have succeeded in
		 * decrypting it.
		 */
		debug("Doing password authentication.");

		/*
		 * The server sent us SSH_SMSG_FAILURE, which means that authentication is
		 * required.
		 */
		CMSG_AUTH_PASSWORD password_packet = new CMSG_AUTH_PASSWORD(password_);
		_ssh_out.writePacket(password_packet);

		Packet packet = readNextPacket();
		if (packet.getPacketType() != SSH_SMSG_SUCCESS) {
			debug("User authentication failure");
			return false;
		}

		_phase = PHASE_PREPARATORY;
		return true;
	}

	/**
	 * Authenticate the user using RSA authentication. This method must be called
	 * after declareUser() has returned successfully with a return value of
	 * <code>true</code>.
	 * 
	 * @return true if the server authenticated us and authorized us to log in.
	 * @exception IOException          if a network error occurs.
	 * @exception SSHProtocolException is a malformed/invalid SSH packet is
	 *                                 received.
	 */
	public boolean authenticateUser(RSAPrivateKey privateKey_)
			throws IOException, SSHProtocolException, SSHAuthFailedException {
		if (_phase != PHASE_AUTHENTICATE) {
			throw new RuntimeException("SSH protocol in invalid state for user authentication");
		}

		/*
		 * The server has sent us an encrypted packet and we have succeeded in
		 * decrypting it. Now declare the user name.
		 */
		debug("Trying RSA authentication with key '" + privateKey_.getComment() + "'");

		/*
		 * The server sent us SSH_SMSG_FAILURE, which means that authentication is
		 * required.
		 */
		byte[] modulus = privateKey_.getModulus();
		CMSG_AUTH_RSA auth_rsa = new CMSG_AUTH_RSA(modulus);
		_ssh_out.writePacket(auth_rsa);

		Packet packet = readNextPacket();
		int packet_type = packet.getPacketType();
		if (packet_type == SSH_SMSG_FAILURE) {
			debug("Server refused our key");
			return false;
		} else if (packet_type != SSH_SMSG_AUTH_RSA_CHALLENGE) {
			throw new SSHProtocolException("Received packet type " + packet_type
					+ ", expected SSH_SMSG_AUTH_RSA_CHALLENGE or SSH_SMSG_FAILURE");
		}
		debug("Received RSA challenge from server.");

		SMSG_AUTH_RSA_CHALLENGE challenge_packet = (SMSG_AUTH_RSA_CHALLENGE) packet;
		byte[] challenge = challenge_packet.getChallenge();

		/*
		 * Now we must decrypt the challenge using our private key, compute the MD5
		 * checksum of the challenge plus the session id, and send back the resulting 16
		 * bytes.
		 */
		byte[] serverRandomBytes = RSAAlgorithm.encrypt(challenge, privateKey_.getExponent(), privateKey_.getModulus());

		serverRandomBytes = RSAAlgorithm.stripPKCSPadding(serverRandomBytes);

		byte[] toBeHashed = SSHMisc.concatenate(serverRandomBytes, _session_id);

		byte[] response_bytes;
		try {
			MessageDigest md5 = MessageDigest.getInstance("MD5");
			response_bytes = md5.digest(toBeHashed);
		} catch (NoSuchAlgorithmException e) {
			// Should never happen
			throw new SSHProtocolException("MD5 algorithm not supported");
		}

		CMSG_AUTH_RSA_RESPONSE response_packet = new CMSG_AUTH_RSA_RESPONSE(response_bytes);
		_ssh_out.writePacket(response_packet);

		packet = readNextPacket();
		packet_type = packet.getPacketType();
		if (packet_type == SSH_SMSG_FAILURE) {
			debug("RSA authentication failed");
			return false;
		} else if (packet_type != SSH_SMSG_SUCCESS) {
			throw new SSHProtocolException(
					"Received packet type " + packet_type + ", expected SSH_MSG_SUCCESS or SSH_SMSG_FAILURE");
		}

		_phase = PHASE_PREPARATORY;
		return true;
	}

	/**
	 * Performs preparatory operations such as requesting compression and
	 * port-forwarding. This method must be called after the user has been
	 * successfully authenticated.
	 */
	public void preparatoryOperations() throws IOException, SSHProtocolException, SSHSetupException {
		if (_phase != PHASE_PREPARATORY) {
			throw new RuntimeException("Protocol not in preparatory phase");
		}

		byte[] terminalModes = { CMSG_REQUEST_PTY.ICRNL, 1, CMSG_REQUEST_PTY.ISIG, 1, CMSG_REQUEST_PTY.ICANON, 1,
				CMSG_REQUEST_PTY.ECHO, 1, CMSG_REQUEST_PTY.ECHOE, 1, CMSG_REQUEST_PTY.ECHOK, 1, CMSG_REQUEST_PTY.ECHONL,
				1, CMSG_REQUEST_PTY.OPOST, 1, CMSG_REQUEST_PTY.ONLCR, 1, CMSG_REQUEST_PTY.CS8, 1,
				CMSG_REQUEST_PTY.TTY_OP_END };

		int columns = _options.getTerminalSize().width;
		int rows = _options.getTerminalSize().height;
		CMSG_REQUEST_PTY pty_packet = new CMSG_REQUEST_PTY(_options.getTerminalType(), rows, columns, terminalModes);

		debug("Requesting pty.");
		_ssh_out.writePacket(pty_packet);

		Packet packet = readNextPacket();
		if (packet.getPacketType() != SSH_SMSG_SUCCESS) {
			throw new SSHProtocolException("server refused to allocate pseudo-tty");
		}

		/*
		 * Request the local port-forwardings that were specified by the user.
		 */
		Iterator<?> iter = _options.getLocalForwardings();
		while (iter.hasNext()) {
			PortForwarding pf = (PortForwarding) iter.next();
			Channel channel = null;
			try {
				channel = new Channel(this, pf.getListenPort(), pf.getHostname(), pf.getHostPort());
			} catch (java.net.BindException e) {
				throw new java.net.BindException("Cannot bind to port " + pf.getListenPort() + "; " + e.getMessage());
			}

			channel.setDaemon(true);
			channel.start();
		}

		/*
		 * Request the remote port-forwardings that were specified by the user.
		 */
		iter = _options.getRemoteForwardings();
		while (iter.hasNext()) {
			PortForwarding pf = (PortForwarding) iter.next();
			debug("Requesting forwarding of remote port " + pf.getListenPort() + " to " + pf.getHostname() + ":"
					+ pf.getHostPort());

			CMSG_PORT_FORWARD_REQUEST forward_request = new CMSG_PORT_FORWARD_REQUEST(pf.getListenPort(),
					pf.getHostname(), pf.getHostPort());
			_ssh_out.writePacket(forward_request);

			if (readNextPacket().getPacketType() != SSH_SMSG_SUCCESS) {
				throw new SSHSetupException("Server refused to forward port " + "(listen-port=" + pf.getListenPort()
						+ " hostname=" + pf.getHostname() + " host-port=" + pf.getHostPort() + ")");
			}
		}

		/*
		 * Enable compression if it was requested by the user.
		 */
		if (_options.compressionEnabled()) {
			debug("Requesting compression.");
			CMSG_REQUEST_COMPRESSION compression_request = new CMSG_REQUEST_COMPRESSION(6);
			_ssh_out.writePacket(compression_request);

			if (readNextPacket().getPacketType() == SSH_SMSG_SUCCESS) {
				_ssh_out.setCompression(6);
				_ssh_in.setCompression();
			}
		}

		_phase = PHASE_READY;
		return;
	}

	/**
	 * Start an interactive session. This method blocks until the session is
	 * terminated. The method must be called after the preparatory phase has
	 * completed successfully.
	 */
	public void execShell(String[] commands, String[] end) throws IOException, SSHProtocolException {
		if (_phase != PHASE_READY) {
			throw new RuntimeException("Protocol not in correct state");
		}

		/*
		 * Request the server to start up an interactive shell for us.
		 */
		debug("Requesting shell.");
		CMSG_EXEC_SHELL shell_packet = new CMSG_EXEC_SHELL();
		_ssh_out.writePacket(shell_packet);

		/*
		 * If the keepalive timeout is nonzero, start a thread to send SSH_MSG_IGNORE
		 * packets when the session has been idle for the specified time.
		 */
		int timeout = _options.getKeepaliveTimeout();
		if (timeout != 0) {
			_keepaliveThread = new KeepaliveThread(timeout);
			_keepaliveThread.setDaemon(true);
			_keepaliveThread.start();
		}

		/*
		 * Read packets from the network input stream, decrypt them and process them.
		 */
		debug("Entering interactive session.");
		int index = 0;
		for (;;) {
			Packet packet = null;
			try {
				packet = readNextPacket();
			} catch (EOFException e) {
				break; // the server closed the connection.
			}

			if (packet instanceof IInteractivePacket == false) {
				throw new SSHProtocolException(
						"packet type " + packet.getPacketType() + " received in interactive mode");
			}

			/*
			 * All packets that implement the IInteractivePacket interface implement the
			 * processPacket() method.
			 */
			((IInteractivePacket) packet).processPacket(this);

			if (packet.getPacketType() == SSH_SMSG_EXITSTATUS) {// 退出状态
				SMSG_EXITSTATUS status_packet = (SMSG_EXITSTATUS) packet;
				debug("Exit status " + status_packet.getExitStatus());
				break;
			}
			String s = new String(packet.getData());
			int i = 0;
			for (; i < end.length; ++i) {
				if (s.trim().endsWith(end[i])) {
					break;
				}
			}
			if (i < end.length) {
				if (index < commands.length) {
					_ssh_out.writePacket(new CMSG_STDIN_DATA((commands[index++] + "\n").getBytes()));
				} else {
					break;
				}
			}
		} // end for loop

		/*
		 * The server closed the network connection; terminate the thread.
		 */
		return;
	}

	/**
	 * Starts executing the given command, and enters interactive session mode. On
	 * UNIX, the command is run as "&lt;shell&gt; -c &lt;command&gt;" where
	 * &lt;shell&gt; is the user's login shell.
	 * <p>
	 *
	 * This method is an alternative to <code>execShell()</code>. It must be called
	 * after the preparatory operations have completed successfully.
	 */
	public void execCmd(String command_) throws IOException, SSHProtocolException {
		if (_phase != PHASE_READY) {
			throw new RuntimeException("Protocol not in correct state");
		}

		/*
		 * Request the server to start up an interactive shell for us.
		 */
		debug("Sending command: " + command_);
		CMSG_EXEC_CMD cmd_packet = new CMSG_EXEC_CMD(command_);
		_ssh_out.writePacket(cmd_packet);

		/*
		 * Read packets from the network input stream, decrypt them and process them.
		 */
		debug("Entering interactive session.");
		for (;;) {
			Packet packet = null;
			try {
				packet = readNextPacket();
			} catch (EOFException e) {
				break; // the server closed the connection.
			} catch (SocketException e) {
				break; // the server closed the connection.
			}

			if (packet instanceof IInteractivePacket == false) {
				throw new SSHProtocolException(
						"packet type " + packet.getPacketType() + " received in interactive mode");
			}

			((IInteractivePacket) packet).processPacket(this);

			if (packet.getPacketType() == SSH_SMSG_EXITSTATUS) {
				SMSG_EXITSTATUS status_packet = (SMSG_EXITSTATUS) packet;
				debug("Exit status " + status_packet.getExitStatus());
				_ssh_out.writePacket(new CMSG_EXIT_CONFIRMATION());
			}
		}
	}

	/**
	 * Registers an open SSH channel (encrypted tunnel).
	 */
	public void registerOpenChannel(OpenChannel channel_) {
		_channelManager.registerOpenChannel(channel_);
	}

	/**
	 * Deregisters an open SSH chanel.
	 */
	public void removeOpenChannel(OpenChannel channel_) {
		_channelManager.removeOpenChannel(channel_);
	}

	/**
	 * Finds the open channel with the specified channel number.
	 */
	public OpenChannel findOpenChannel(int channel_number_) {
		return _channelManager.findOpenChannel(channel_number_);
	}

	/**
	 * This method is called when a SSH_MSG_PORT_OPEN message is received; it
	 * returns true if the port open request matches one of the local port
	 * forwardings specified by the user.
	 */
	public boolean isPortOpenAllowed(String hostname_, int port_) {

		// Delegate to the Options member variable.
		return _options.isPortOpenAllowed(hostname_, port_);
	}

	/**
	 * Returns the SSH_SMSG_PUBLIC_KEY packet that was sent by the server.
	 */
	public SMSG_PUBLIC_KEY getServerKeyPacket() {
		return _server_public_key_packet;
	}

	/**
	 * Private helper method to read the next SSH packet. DEBUG packets are logged
	 * but are not returned to the caller.
	 */
	private Packet readNextPacket() throws IOException, SSHProtocolException {
		Packet packet = null;
		while (true) {
			packet = _ssh_in.readPacket();

			// Reception of any SSH packet should reset the keepalive timer.
			if (_keepaliveThread != null) {
				_keepaliveThread.interrupt();
			}

			int packet_type = packet.getPacketType();
			if (packet_type == SSH_MSG_DEBUG) {
				debug(((MSG_DEBUG) packet).getMessage());
			} else if (packet_type != SSH_MSG_IGNORE)
				break;
		}
		return packet;
	}

	/**
	 * Display the specified debugging message.
	 */
	public void debug(String string_) {
		if (_options.getDebug())
			System.err.println("debug: " + string_ + "\r\n");
	}

	/**
	 * A nonstatic inner class which sends SSH_MSG_IGNORE packets to keep the
	 * session alive.
	 */
	private class KeepaliveThread extends Thread {
		private int _seconds;

		public KeepaliveThread(int seconds_) {
			_seconds = seconds_;
		}

		public void run() {
			for (;;) {
				try {
					Thread.sleep(_seconds * 1000L);
				} catch (InterruptedException e) {
					/*
					 * The sleep was interrupted by transmission or reception of a packet. Start a
					 * new sleep.
					 */
					continue;
				}
				if (_socket == null || _socket.isClosed() || !_socket.isConnected()) {
					break;
				}
				/*
				 * If we get here, it means that we completed our sleep without being
				 * interrupted by reception of an SSH packet. So send a SSH_MSG_IGNORE packet to
				 * keep the session alive.
				 */
				try {
					_ssh_out.writePacket(new MSG_IGNORE());
				} catch (IOException e) {
					e.printStackTrace();
					break;
				}
			} // end for loop
		}
	}

	public void close() throws IOException {
		if (_socket != null) {
			_socket.close();
		}
	}

	// ====================================================================
	// INSTANCE VARIABLES

	/**
	 * This is the network socket connected to the remote server.
	 */
	private Socket _socket;

	/**
	 * Encapsulates all the command-line options.
	 */
	private Options _options;

	/**
	 * This is the input stream associated with the SSH network connection.
	 */
	private BufferedInputStream _instream;

	/**
	 * This is the output stream associated with the SSH network connection.
	 */
	private BufferedOutputStream _outstream;

	/**
	 * The public key packet received from the server is stored here for later use.
	 */
	private SMSG_PUBLIC_KEY _server_public_key_packet;

	/**
	 * The session id (extracted from the SSH_CMSG_SESSION_KEY packet) is stored
	 * here for later use.
	 */
	private byte[] _session_id;

	private SSHInputStream _ssh_in;

	private SSHOutputStream _ssh_out;

	private STDOUT_InputStream _stdout = new STDOUT_InputStream();

	private STDIN_OutputStream _stdin = null;

	private StringBuffer _id_string_received = new StringBuffer();

	private static String ID_STRING_SENT = "SSH-1.5-JSSH 0.9.5 (2002/10/05)";

	private ChannelManager _channelManager = new ChannelManager();

	private KeepaliveThread _keepaliveThread = null;

	private int _phase = PHASE_EXCHANGE_ID_STRINGS;

	// The protocol must proceed through each of these phases in turn.
	private static final int PHASE_EXCHANGE_ID_STRINGS = 1;
	private static final int PHASE_RECEIVE_SERVER_KEY = 2;
	private static final int PHASE_SEND_SESSION_KEY = 3;
	private static final int PHASE_DECLARE_USER = 4;
	private static final int PHASE_AUTHENTICATE = 5;
	private static final int PHASE_PREPARATORY = 6;
	private static final int PHASE_READY = 7;
}

使用方式如下

            Options options = new Options();
            options.setHostname(machineip);
            options.setUser(username);
            options.setPort(connect_port);
            options.setTerminalType("xterm");

            SSHClient client = new SSHClient(options);
            try {
                client.connect(password);
                String[] commands = command.split(";");
                if (commands.length == 1) {
                    options.setCommand(commands[0]);
                    result = client.execCmd();
                } else {
                    result = client.execShell(commands, new String[] { ">", "]" });
                }
            } catch (Exception e) {
                if (e instanceof java.net.ConnectException) {
                    throw new CommandException("Cannot connect to " + options.getHostname() + ": " + e.getMessage());
                } else if (e instanceof IOException) {
                    throw new CommandException("IOException: " + e.getMessage());
                } else if (e instanceof SSHSetupException) {
                    throw new CommandException("SSHSetupException: " + e.getMessage());
                } else if (e instanceof SSHProtocolException) {
                    throw new CommandException("SSHProtocolException: " + e.getMessage());
                } else if (e instanceof SSHAuthFailedException) {
                    throw new CommandException("Authentication failed");
                } else {
                    throw e;
                }
            } finally {// 操作结束后,一定要关闭
                client.close();
            }

执行结果是之前的,不过我还是留着吧:

  Connecting to 不给看...  Connected
-------------
Note: The max number of VTY users is 5, and the current number
      of VTY users on line is 1.
NOTICE:This is a private communication system.
       Unauthorized access or use may lead to prosecution.
HRP_M<不给看>
16:42:27  2019/12/31
16:42:27 beijing Tue 2019/12/31
Time Zone : beijing add 08:00:00
HRP_M<不给看>
-------------

好了,以上就是我的方法,要是有更简单的方法也可以告诉我,就是那种有专门的封装好的jar包,跟ssh2一样简便就更好了,网上还有一大堆教程。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值