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 + -
显示快捷键?