sshclient.java

来自「一个非常好的ssh客户端实现」· Java 代码 · 共 1,227 行 · 第 1/3 页

JAVA
1,227
字号
	  serverAddr = sshSocket.getInetAddress();	  if(interactor != null)	      interactor.report("Connecting through proxy at " + serverAddr.getHostAddress() +				":" + sshSocket.getPort());      }      if(releaseConnection) {	  return;      }      boot(haveCnxWatch, sshSocket);    } catch (IOException e) {      if(sshSocket != null)	sshSocket.close();      disconnect(false);      if(controller != null) {	  controller.killListenChannels();      }      controller = null;      throw e;    }  }  public void boot(boolean haveCnxWatch, Socket tpSocket) throws IOException {      try {	  this.sshSocket = tpSocket;	  sshIn  = new BufferedInputStream(sshSocket.getInputStream(), 8192);	  sshOut = new BufferedOutputStream(sshSocket.getOutputStream());	  negotiateVersion();	  // We now have a physical connection to a sshd, report this to the SSHClientUser	  //	  isConnected = true;	  if(interactor != null)	      interactor.connected(this);	  String userName = authenticator.getUsername(user);	  receiveServerData();	  initiatePlugins();	  cipherType = authenticator.getCipher(user);	  // Check that selected cipher is supported by server	  //	  if(!isCipherSupported(cipherType))	      throw new IOException("Sorry, server does not support the '" +				    getCipherName(authenticator.getCipher(user)) + "' cipher.");	  generateSessionId();	  generateSessionKey();	  initClientCipher();	  sendSessionKey(cipherType);	  // !!!	  // At this stage the communication is encrypted	  // !!!	  authenticateUser(userName);	  //	  //	  requestCompression(user.getCompressionLevel());	  controller = new SSHChannelController(this, sshIn, sshOut,						sndCipher, sndComp,						rcvCipher, rcvComp,						console, haveCnxWatch);	  initiateSession();	  if(console != null)	      console.serverConnect(controller, sndCipher);	// We now open the SSH-protocol fully, report to SSHClientUser	//	  isOpened = true;	  if(interactor != null)	      interactor.open(this);	// Start "heartbeat" if needed	//	  setAliveInterval(user.getAliveInterval());	  controller.start();      } catch (IOException e) {	  if(sshSocket != null)	      sshSocket.close();	  disconnect(false);	  if(controller != null) {	      controller.killListenChannels();	  }	  controller = null;	  throw e;      }  }  protected void disconnect(boolean graceful) {    if(!isConnected)      return;    isConnected   = false;    isOpened      = false;    gracefulExit  = graceful;    srvVersionStr = null;    setAliveInterval(0); // Stop "heartbeat"...    if(interactor != null) {	interactor.disconnected(this, graceful);	String msg = "";	if(sndComp != null || rcvComp != null) {	    for(int i = 0; i < 2; i++) {		SSHCompressor comp = (i == 0 ? sndComp : rcvComp);		if(comp != null) {		    long compressed, uncompressed;		    compressed   = comp.numOfCompressedBytes();		    uncompressed = comp.numOfUncompressedBytes();		    msg += (i == 0 ? "outgoing" : "\r\nincoming") +			" raw data (bytes) = " + uncompressed +			", compressed = " + compressed + " (" +			((compressed * 100) / uncompressed) + "%)";		}	    }	    interactor.report(msg);	}    }    rcvComp = sndComp = null;  }  void negotiateVersion() throws IOException {    byte[] buf = new byte[256];    int    len;    String verStr;    try {         len = sshIn.read(buf);        srvVersionStr = new String(buf, 0, len);    } catch (Throwable t) {        throw new IOException("Could not negotiate SSH version with server");    }            try {	int l = srvVersionStr.indexOf('-');	int r = srvVersionStr.indexOf('.');	srvVersionMajor = Integer.parseInt(srvVersionStr.substring(l + 1, r));	l = r;	r = srvVersionStr.indexOf('-', l);	if(r == -1) {	    srvVersionMinor = Integer.parseInt(srvVersionStr.substring(l + 1));	} else {	    srvVersionMinor = Integer.parseInt(srvVersionStr.substring(l + 1, r));	}    } catch (Throwable t) {      throw new IOException("Server version string invalid: " + srvVersionStr);    }    if(srvVersionMajor > 1) {      throw new IOException("This server doesn't support ssh1, connect with " +			    "ssh2 enabled");    } else if(srvVersionMajor < 1 || srvVersionMinor < 5) {      throw new IOException("Server's protocol version (" + srvVersionMajor + "-" +			    srvVersionMinor + ") is too old, please upgrade");    }    // Strip white-space    srvVersionStr = srvVersionStr.trim();    verStr = getVersionId(true);    verStr += "\n";    buf    = verStr.getBytes();    sshOut.write(buf);    sshOut.flush();  }  void receiveServerData() throws IOException {    SSHPduInputStream pdu = new SSHPduInputStream(SMSG_PUBLIC_KEY, null, null);    pdu.readFrom(sshIn);    int bits;    BigInteger e, n;    srvCookie = new byte[8];    pdu.read(srvCookie, 0, 8);    bits = pdu.readInt();    e = pdu.readBigInteger();    n = pdu.readBigInteger();    srvServerKey = new com.mindbright.security.publickey.RSAPublicKey(n, e);    bits = pdu.readInt();    e = pdu.readBigInteger();    n = pdu.readBigInteger();    srvHostKey = new com.mindbright.security.publickey.RSAPublicKey(n, e);    int keyLenDiff = Math.abs(srvServerKey.getModulus().bitLength() -			      srvHostKey.getModulus().bitLength());    if(keyLenDiff < 24) {      throw new IOException("Invalid server keys, difference in sizes must be at least 24 bits");    }    if(!authenticator.verifyKnownHosts(srvHostKey)) {      throw new IOException("Verification of known hosts failed");    }    protocolFlags      = pdu.readInt();    supportedCiphers   = pdu.readInt();    supportedAuthTypes = pdu.readInt();    // OUCH: Support SDI patch from ftp://ftp.parc.xerox.com://pub/jean/sshsdi/    // (we want the types to be in sequence for simplicity, kludge but simple)    //    if((supportedAuthTypes & (1 << 16)) != 0) {	supportedAuthTypes = ((supportedAuthTypes & 0xffff) | (1 << AUTH_SDI));    }  }  void generateSessionKey() {    sessionKey = new byte[SESSION_KEY_LENGTH / 8];    rand.nextBytes(sessionKey);  }  void sendSessionKey(int cipherType) throws IOException {    byte[]             key = new byte[sessionKey.length + 1];    BigInteger         encKey;    SSHPduOutputStream pdu;    key[0] = 0;    System.arraycopy(sessionKey, 0, key, 1, sessionKey.length);    for(int i = 0; i < sessionId.length; i++)      key[i + 1] ^= sessionId[i];    encKey = new BigInteger(key);    int serverKeyByteLen = (srvServerKey.getModulus().bitLength() + 7) / 8;    int hostKeyByteLen   = (srvHostKey.getModulus().bitLength() + 7) / 8;    try {	if(serverKeyByteLen < hostKeyByteLen) {	    BigInteger padded;	    padded = RSAAlgorithm.addPKCS1Pad(encKey, 2, serverKeyByteLen, rand);	    encKey = RSAAlgorithm.doPublic(padded,					   srvServerKey.getModulus(),					   srvServerKey.getPublicExponent());	    padded = RSAAlgorithm.addPKCS1Pad(encKey, 2, hostKeyByteLen, rand);	    encKey = RSAAlgorithm.doPublic(padded,					   srvHostKey.getModulus(),					   srvHostKey.getPublicExponent());	} else {	    BigInteger padded;	    padded = RSAAlgorithm.addPKCS1Pad(encKey, 2, hostKeyByteLen, rand);	    encKey = RSAAlgorithm.doPublic(padded,					   srvHostKey.getModulus(),					   srvHostKey.getPublicExponent());	    padded = RSAAlgorithm.addPKCS1Pad(encKey, 2, serverKeyByteLen, rand);	    encKey = RSAAlgorithm.doPublic(padded,					   srvServerKey.getModulus(),					   srvServerKey.getPublicExponent());	}    } catch (SignatureException e) {	throw new IOException(e.getMessage());    }    pdu = new SSHPduOutputStream(CMSG_SESSION_KEY, null, null, rand);    pdu.writeByte((byte)cipherType);    pdu.write(srvCookie, 0, srvCookie.length);    pdu.writeBigInteger(encKey);    // !!! TODO: check this pdu.writeInt(PROTOFLAG_SCREEN_NUMBER | PROTOFLAG_HOST_IN_FWD_OPEN);    pdu.writeInt(protocolFlags);    pdu.writeTo(sshOut);    // !!!    // At this stage the communication is encrypted    // !!!    if(!isSuccess())      throw new IOException("Error while sending session key!");  }  void authenticateUser(String userName) throws IOException {    SSHPduOutputStream outpdu;    usedOTP = false;    outpdu = new SSHPduOutputStream(CMSG_USER, sndCipher, sndComp, rand);    outpdu.writeString(userName);    outpdu.writeTo(sshOut);    if(isSuccess()) {	if(interactor != null)	    interactor.report("Authenticated directly by server, no other authentication required");	return;    }    int[] authType = authenticator.getAuthTypes(user);    for(int i = 0; i < authType.length; i++) {      try {	if(!isAuthTypeSupported(authType[i])) {	    throw new AuthFailException("server does not support '" +					authTypeDesc[authType[i]] + "'");	}	switch(authType[i]) {	case AUTH_PUBLICKEY:	  doRSAAuth(false, userName);	  break;	case AUTH_PASSWORD:	  doPasswdAuth(userName);	  break;	case AUTH_RHOSTS_RSA:	  doRSAAuth(true, userName);	  break;	case AUTH_TIS:	  doTISAuth(userName);	  break;	case AUTH_RHOSTS:	  doRhostsAuth(userName);	  break;	case AUTH_SDI:	  doSDIAuth(userName);	  usedOTP = true;	  break;	case AUTH_KERBEROS:	case PASS_KERBEROS_TGT:	default:	    throw new IOException("We do not support selected authentication type " +				  authTypeDesc[authType[i]]);	}	return;      } catch (AuthFailException e) {	if(i == (authType.length - 1)) {	  throw e;	}	if(interactor != null) {	    interactor.report("Authenticating with " + authTypeDesc[authType[i]] + " failed, " +			      e.getMessage());	}      }    }  }  void doPasswdAuth(String userName) throws IOException {    SSHPduOutputStream outpdu;    String password;    password = authenticator.getPassword(user);    outpdu = new SSHPduOutputStream(CMSG_AUTH_PASSWORD, sndCipher, sndComp,				    rand);    outpdu.writeString(password);    outpdu.writeTo(sshOut);    if(!isSuccess())      throw new AuthFailException();  }  void doRhostsAuth(String userName) throws IOException {    SSHPduOutputStream outpdu;    outpdu = new SSHPduOutputStream(CMSG_AUTH_RHOSTS, sndCipher, sndComp, rand);    outpdu.writeString(userName);    outpdu.writeTo(sshOut);    if(!isSuccess())      throw new AuthFailException();  }  void doTISAuth(String userName) throws IOException {    SSHPduOutputStream outpdu;    String prompt;    String response;    outpdu = new SSHPduOutputStream(CMSG_AUTH_TIS, sndCipher, sndComp, rand);    outpdu.writeTo(sshOut);    SSHPduInputStream inpdu = new SSHPduInputStream(MSG_ANY, rcvCipher, rcvComp);    inpdu.readFrom(sshIn);    if(inpdu.type == SMSG_FAILURE)      throw new AuthFailException("TIS authentication server not reachable or user unknown");    else if(inpdu.type != SMSG_AUTH_TIS_CHALLENGE)      throw new IOException("Protocol error, expected TIS challenge but got " + inpdu.type);    prompt = inpdu.readString();    response = authenticator.getChallengeResponse(user, prompt);    outpdu = new SSHPduOutputStream(CMSG_AUTH_TIS_RESPONSE, sndCipher, sndComp,				    rand);    outpdu.writeString(response);    outpdu.writeTo(sshOut);     if(!isSuccess())       throw new AuthFailException();  }  void doRSAAuth(boolean rhosts, String userName) throws IOException {    SSHPduOutputStream outpdu;    SSHRSAKeyFile      keyFile = authenticator.getIdentityFile(user);    RSAPublicKey       pubKey  = keyFile.getPublic();    if(rhosts) {      outpdu = new SSHPduOutputStream(CMSG_AUTH_RHOSTS_RSA, sndCipher, sndComp,				      rand);      outpdu.writeString(userName);

⌨️ 快捷键说明

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