⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 ssh2transport.java

📁 一个非常好的ssh客户端实现
💻 JAVA
📖 第 1 页 / 共 4 页
字号:
/****************************************************************************** * * Copyright (c) 1999-2003 AppGate Network Security AB. All Rights Reserved. *  * This file contains Original Code and/or Modifications of Original Code as * defined in and that are subject to the MindTerm Public Source License, * Version 2.0, (the 'License'). You may not use this file except in compliance * with the License. *  * You should have received a copy of the MindTerm Public Source License * along with this software; see the file LICENSE.  If not, write to * AppGate Network Security AB, Otterhallegatan 2, SE-41118 Goteborg, SWEDEN * *****************************************************************************/package com.mindbright.ssh2;import java.io.InputStream;import java.io.OutputStream;import java.net.Socket;import java.io.IOException;import java.io.FileOutputStream;import com.mindbright.jca.security.MessageDigest;import com.mindbright.jca.security.SecureRandom;import com.mindbright.jca.security.InvalidKeyException;import com.mindbright.jce.crypto.Cipher;import com.mindbright.jce.crypto.Mac;import com.mindbright.jce.crypto.spec.SecretKeySpec;import com.mindbright.jce.crypto.spec.IvParameterSpec;import com.mindbright.jce.crypto.ShortBufferException;import com.mindbright.util.SecureRandomAndPad;import com.mindbright.util.Queue;import com.mindbright.util.Log;/** * This class implements the transport layer of the secure shell version 2 * (ssh2) protocol stack. It handles the initial negotiation of algorithms for * key exchange, host key type, encryption, message authentication and * compression. It also handles server authentication (through the provided * <code>SSH2TransportEventHandler</code>). * <p> * To create a <code>SSH2Transport</code> instance a TCP connection to the ssh2 * server must first be established using a <code>java.net.Socket</code>. This * socket is passed to the constructor. The constructor is passive in that it * doesn't start any communication. To start the protocol engine and begin * communcation with the server the method <code>boot</code> must be called. In * this method the version information echange is done and two threads are * started which handles the protocol engine and all communication with the * server. * <p> * The operation of the transport layer can be controlled and monitored with * instances of the classes <code>SSH2Preferences</code> and * <code>SSH2TransportEventHandler</code> which are provided to the constructor. * <p> * After initial negotiation the chosen key exchange algorithm is handled by a * subclass of the abstract class <code>SSH2KeyExchanger</code>. When the key * exchange is complete, keys for encryption and message authentication can be * derived.  The communciation at this point is secured with the selected * cipher/mac algorithms. The actual encryption, message authentication and * compression is handled in the class <code>SSH2TransportPDU</code> which is * the data container implementing the specific formatting defined in the * protocol. * <p> * Before the upper layers (i.e. the user authentication and connection layers) * of the protocol can be started the key exchange stage must be completed. This * must be checked by calling the blocking method * <code>waitForKEXComplete</code>. When the key exchange is complete a secure * connection to an authenticated server has been established. The function of * the transport layer at this point is the multiplexing of protocol data units * (referred to as PDU or packet) between the server and the upper layers which * are implemented in the classes <code>SSH2UserAuth</code> and * <code>SSH2Connection</code>. * * @see SSH2TransportEventHandler * @see SSH2Preferences * @see SSH2UserAuth * @see SSH2Connection */public final class SSH2Transport {    private class KeepAliveThread implements Runnable {	private volatile int     interval;	private volatile boolean keepRunning;	protected KeepAliveThread(int interval) {	    this.interval    = interval;	    this.keepRunning = true;	    Thread heartbeat = new Thread(this, "SSH2TransportKeepAlive");	    heartbeat.setDaemon(true);	    heartbeat.setPriority(Thread.MIN_PRIORITY);	    heartbeat.start();	}	protected synchronized void setInterval(int interval) {	    if(interval < 1) {		stop();	    } else {		this.interval = interval;	    }	}	public void run() {	    int inactive = 0;	    while (keepRunning) {		try {		    Thread.sleep(1000);		} catch (InterruptedException e) { /* ignore */ }		if (activity) {		    inactive = 0;		    activity = false;		} else {		    if (++inactive >= interval) {			sendIgnore("heartbeat".getBytes());			Thread.yield();			inactive = 0;			activity = false;		    }		}	    }	}	protected void stop() {	    keepRunning = false;	}	public boolean isRunning() {	    return keepRunning;	}    }    /**     * Context for transport tranceiver/receiver. Holds the encryption     * and compression states.     */    public static class TranceiverContext {	protected Mac                mac;	protected Cipher             cipher;	protected SSH2Compressor     compressor;	public int getCipherBlockSize() {	    return cipher.getBlockSize();	}	public void init(byte[] cKey, byte[] iv, byte[] mKey, int compLevel,			 boolean transmitter)	    throws SSH2Exception	{	    try {		if(cipher != null) {		    cipher.init(transmitter ? Cipher.ENCRYPT_MODE :				Cipher.DECRYPT_MODE,				new SecretKeySpec(cKey, cipher.getAlgorithm()),				new IvParameterSpec(iv));		}		if(mac != null) {		    mac.init(new SecretKeySpec(mKey, mac.getAlgorithm()));		}		if(compressor != null) {		    compressor.init((transmitter ?				     SSH2Compressor.COMPRESS_MODE :				     SSH2Compressor.UNCOMPRESS_MODE),				    compLevel);		}	    } catch (InvalidKeyException e) {		throw new SSH2FatalException("Invalid key in TranceiverContext.init");	    }	}    }    private final static boolean DEBUG_ALL_TX = false;    private final static boolean DEBUG_ALL_RX = false;    private boolean weAreAServer;    private String clientVersion;    private String serverVersion;    private SSH2Preferences ourPrefs;    private SSH2Preferences peerPrefs;    private SSH2TransportEventHandler eventHandler;    private          SSH2KeyExchanger keyExchanger;    private volatile SSH2UserAuth     userAuth;    private volatile SSH2Connection   connection;    private Log tpLog;    protected Socket       tpSocket;    protected InputStream  tpIn;    protected OutputStream tpOut;    private Thread transmitter;    private Thread receiver;    private Queue  txQueue;    private SecureRandomAndPad tpRand;    private KeepAliveThread heartbeat;    private byte[]           sessionId;    private volatile boolean keyExchangeInProgress;    private          boolean keyExchangeOk;    private          Object  keyExchangeMonitor;    private SSH2TransportPDU clientKEXINITPkt;    private SSH2TransportPDU serverKEXINITPkt;    private byte[]           serverPublicKeyBlob;    private TranceiverContext rxContext;    private TranceiverContext txContext;    private int               rxSeqNum;    private int               txSeqNum;    private int               rxNumPacketsSinceKEX;    private int               txNumPacketsSinceKEX;    private int               rxNumBytesSinceKEX;    private int               txNumBytesSinceKEX;    private final static int  PACKETS_BEFORE_REKEY = 2147483647;    private final static int  BYTES_BEFORE_REKEY= 1073741824;    private          Object  disconnectMonitor;    private volatile boolean isConnected;    private volatile boolean isTxUp;    private volatile boolean isRxUp;    private          String  disconnectMessage;    private          boolean activity;    // Incompatibility flags (peer's incompatibility of course :-)    //    public boolean incompatibleSignature;    public boolean incompatibleServiceAccept;    public boolean incompatiblePublicKeyAuth;    public boolean incompatibleHMACKeyLength;    public boolean incompatiblePublicKeyUserId;    public boolean incompatibleChannelOpenFail;    public boolean incompatibleRijndael;    public boolean incompatibleCantReKey;    public boolean incompatibleBuggyChannelClose;    /**     * This is the basic constructor used when default preferences is ok and no     * logging or event handling is needed.     *     * @param tpSocket the connection to the ssh2 server     * @param rand the source of randomness for keys and padding     */    public SSH2Transport(Socket tpSocket, SecureRandomAndPad rand) {	this(tpSocket, new SSH2Preferences(), rand);    }    /**     * This is the basic constructor used when no logging or event handling is     * needed.     *     * @param tpSocket the connection to the ssh2 server     * @param prefs    the protocol preferences     * @param rand     the source of randomness for keys and padding     */    public SSH2Transport(Socket tpSocket, SSH2Preferences prefs,			 SecureRandomAndPad rand) {	this(tpSocket, prefs, null, rand);    }    /**     * This is the constructor used when an event handler is needed but no     * logging.     *     * @param tpSocket     the connection to the ssh2 server     * @param prefs        the protocol preferences     * @param eventHandler the event handler which receives callbacks     * @param rand         the source of randomness for keys and padding     */    public SSH2Transport(Socket tpSocket, SSH2Preferences prefs,			 SSH2TransportEventHandler eventHandler,			 SecureRandomAndPad rand) {	this(tpSocket, prefs, eventHandler, rand,	     new Log(prefs.getIntPreference(SSH2Preferences.LOG_LEVEL)));	String logFile = prefs.getPreference(SSH2Preferences.LOG_FILE);	if(logFile != null) {	    try {		boolean append =		    "true".equals(prefs.				  getPreference(SSH2Preferences.LOG_APPEND));		FileOutputStream log = new FileOutputStream(logFile, append);		tpLog.setLogOutputStream(log);	    } catch (IOException e) {		tpLog.error("SSH2Transport", "<init>", "could't open log file: "			    + e.getMessage());	    }	}    }    /**     * This is the constructor used when both an event handler and logging is     * needed.     *     * @param tpSocket     the connection to the ssh2 server     * @param prefs        the protocol preferences     * @param eventHandler the event handler which receives callbacks     * @param rand         the source of randomness for keys and padding     * @param log          the log handler which receives all logs     */    public SSH2Transport(Socket tpSocket, SSH2Preferences prefs,			 SSH2TransportEventHandler eventHandler,			 SecureRandomAndPad rand, Log log) {	this.disconnectMonitor  = new Object();	this.keyExchangeMonitor = new Object();	this.isConnected        = false;	this.isTxUp             = false;	this.isRxUp             = false;	this.ourPrefs           = prefs;	this.eventHandler       = (eventHandler != null ? eventHandler :				   new SSH2TransportEventAdapter());	this.tpSocket           = tpSocket;	this.tpRand             = rand;	this.tpLog              = log;	// !!! REMOVE	SSH2TransportPDU.pktDefaultSize =	    ourPrefs.getIntPreference(SSH2Preferences.DEFAULT_PKT_SZ);	SSH2TransportPDUPool.POOL_SIZE =	    ourPrefs.getIntPreference(SSH2Preferences.PKT_POOL_SZ);	try {	    setSocketOptions(SSH2Preferences.SOCK_OPT_TRANSPORT, tpSocket);	    this.rxContext =		SSH2TransportPDU.createTranceiverContext("none", "none", "none");	    this.txContext =		SSH2TransportPDU.createTranceiverContext("none", "none", "none");	    this.tpIn  = tpSocket.getInputStream();	    this.tpOut = tpSocket.getOutputStream();	} catch (Exception e) {	    // !!! TODO: pathological, fixit!!!	}    }    /**     * Starts the protocol engine and begins communication with the server. It     * completes the version negotiation and starts two threads which handles     * the protocol engine and all communication with the server. The key     * exchange is started here.     *     * @exception SSH2Exception if a fatal error occurs such as an I/O error or     * a protocol mismatch.     */    public void boot() throws SSH2Exception {	synchronized(disconnectMonitor) {	    if(isConnected) {		throw new SSH2FatalException("Already booted");	    }	    isConnected = true;	}	try {	    negotiateVersion();	} catch (IOException e) {	    throw new SSH2FatalException("I/O error in version negotiation", e);	}	transmitter = new Thread(new Runnable() {	    public void run() {		transportTransmitLoop();	    }	}, "SSH2TransportTX");	txQueue = new	    Queue(ourPrefs.getIntPreference(SSH2Preferences.QUEUE_DEPTH),		  ourPrefs.getIntPreference(SSH2Preferences.QUEUE_HIWATER));	transmitter.start();	// Note we start the receiver AFTER we do startKeyExchange() to avoid	// race with startKeyExchange() in receiver	//	startKeyExchange();	receiver = new Thread(new Runnable() {	    public void run() {		transportReceiveLoop();	    }	}, "SSH2TransportRX");	receiver.start();    }    public void setSocketOptions(String desc, Socket sock) throws IOException {	String  prefix = SSH2Preferences.SOCK_OPT + desc;	String  val    = ourPrefs.getPreference(prefix + "." +						SSH2Preferences.SO_TCP_NODELAY);	if(val != null) {	    sock.setTcpNoDelay(Boolean.valueOf(val).booleanValue());	}	/* TODO more socket options goes here... */    }    /**     * Gets the session identifier calculated at key exchange as defined in the     * protool spec.     *     * @return the session identifier as a byte array.     */    public byte[] getSessionId() {	byte[] id = sessionId;	if(!incompatiblePublicKeyUserId) {	    SSH2DataBuffer buf =		new SSH2DataBuffer(sessionId.length + 4);	    buf.writeString(sessionId);	    id = buf.readRestRaw();	}	return id;    }    /**     * Gets the PDU containing the key exchange initialization (KEXINIT) sent by     * the client.     *     * @return the PDU containing the KEXINIT packet     */    public SSH2TransportPDU getClientKEXINITPDU() {	return clientKEXINITPkt;    }    /**     * Gets the PDU containing the key exchange initialization (KEXINIT) sent by     * the server.     *     * @return the PDU containing the KEXINIT packet.     */    public SSH2TransportPDU getServerKEXINITPDU() {	return serverKEXINITPkt;    }    /**     * Gets the client's version string     *     * @return the client's version string     */

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -