SSH(Secure Shell,安全外殼協議),是專為遠程登錄會話和其他網絡服務提供安全性的應用層協議。在日常開發中,包括登錄遠程服務器、遠程執行命令腳本、文件傳輸等,都使用了 SSH 協議的實現。而 SSHJ 就是 SSH 協議的一個 JAVA 語言實現,其功能齊全,對 SSH 協議的特性實現全面,可以很方便地在代碼中實現 SSH 相關功能應用,值得 Java 開發者了解使用。
SSH協議
簡介
SSHJ 是 hierynomus 在 Github 上開源的 Java SSH 庫,項目位于 https://github.com/hierynomus/sshj,目前版本為 v0.29.0。
SSHJ 功能齊全,支持從 known_hosts 文件讀取驗證公鑰,支持公鑰、密碼和交互式的驗證方式,支持命令、子系統和 Shell Channel,支持本地和遠程端口轉發,支持 SCP 安全拷貝協議,支持從版本 0 到 3 的完全的 SFTP 安全文件傳輸協議,支持廣泛的加密、簽名、壓縮等的算法實現。
SSH協議和SSHJ庫
安裝
SSHJ 使用方便,可以使用 Maven 添加到項目依賴,在 pom.xml 中添加
<dependency>
<groupId>com.hierynomus</groupId>
<artifactId>sshj</artifactId>
<version>0.29.0</version>
</dependency>
SSHJ 的主要依賴是 SLF4J,另外,一些加密算法可能會需要 BouncyCastle,而 zlib 壓縮則需要依賴 JZlib。
SSHJ 也可以自行編譯,需要 Java6 及以上環境,并安裝有 Unlimited strength Java Cryptography Extensions (JCE) 加密擴展包,運行命令
./gradlew clean build
SSH協議與SSHJ庫
實例
SSHJ中的主要接口由 SSHClient 提供,其作為客戶端用于連接 SSH 服務,每次連接都會生成一個會話 session,在一個 session 中進行具體操作。我們來看一個基本的使用例子:
package net.schmizz.sshj.examples;
import net.schmizz.sshj.SSHClient;
import net.schmizz.sshj.common.IOUtils;
import net.schmizz.sshj.connection.channel.direct.Session;
import net.schmizz.sshj.connection.channel.direct.Session.Command;
import java.io.Console;
import java.io.IOException;
import java.util.concurrent.TimeUnit;
/** 執行遠程命令 */
public class Exec {
private static final Console con = System.console();
public static void main(String... args)
throws IOException {
final SSHClient ssh = new SSHClient();
ssh.loadKnownHosts();
ssh.connect("localhost");
Session session = null;
try {
ssh.authPublickey(System.getProperty("user.name"));
session = ssh.startSession();
final Command cmd = session.exec("ping -c 1 toutiao.com");
con.writer().print(IOUtils.readFully(cmd.getInputStream()).toString());
cmd.join(5, TimeUnit.SECONDS);
con.writer().print("n** exit status: " + cmd.getExitStatus());
} finally {
try {
if (session != null) {
session.close();
}
} catch (IOException e) {
// 處理異常
}
ssh.disconnect();
}
}
}
這是使用 SSHJ 在遠程服務器上執行命令的例子。首先創建一個 SSHClient,再加載 know_hosts,并連接到 localhost 所在的 SSH 服務。然后,使用本機用戶名對應的公鑰進行 SSH 登錄驗證,并啟動會話。成功建立連接后,使用 session 的 exec 接口在 SSH 服務所在遠程執行了一條 ping 命令,并從遠程讀取命令行輸出,返回到本地命令行進行打印。最終,在完成任務后,關閉會話并斷開連接。以此為基礎可以實現遠程命令的程序化實現,把需要手動登錄 SSH 并執行命令的過程自動化。
SSHJ 實現了 SCP 安全拷貝協議,用于加密的文件在本地和遠程之間拷貝復制。在 SSHClient 連接成功之后,使用 SCPFileTransfer 實現文件的上傳和下載:
// SCP下載文件
ssh.newSCPFileTransfer().download("test_file", new FileSystemFile("/tmp/"));
// SCP上傳文件
ssh.newSCPFileTransfer().upload(new FileSystemFile(src), "/tmp/");
SSHJ 還實現了 SFTP 安全文件傳輸協議。與 SCP 相比,SFTP 可靠性高,可斷點續傳,支持更加廣泛的遠程文件操作。SSHJ 中使用 SFTPClient 來作為 SFTP 的客戶端,在 SSHClient 連接成功后,創建 SFTPClient,并使用 put 和 get 進行上傳和下載:
// SFTP下載
final SFTPClient sftp = ssh.newSFTPClient();
try {
sftp.get("test_file", new FileSystemFile("/tmp"));
} finally {
sftp.close();
}
// SFTP上傳
final SFTPClient sftp = ssh.newSFTPClient();
try {
sftp.put(new FileSystemFile(src), "/tmp");
} finally {
sftp.close();
}
利用 SSHJ,我們還就可以很方便地實現一個交互式 SSH 客戶端,在本地命令行上實現對遠程命令行的交互:
package net.schmizz.sshj.examples;
import net.schmizz.sshj.SSHClient;
import net.schmizz.sshj.common.StreamCopier;
import net.schmizz.sshj.connection.channel.direct.Session;
import net.schmizz.sshj.connection.channel.direct.Session.Shell;
import net.schmizz.sshj.transport.verification.ConsoleKnownHostsVerifier;
import net.schmizz.sshj.transport.verification.OpenSSHKnownHosts;
import java.io.File;
import java.io.IOException;
import net.schmizz.sshj.common.LoggerFactory;
/** 交互式SSH客戶端 */
class RudimentaryPTY {
public static void main(String... args)
throws IOException {
final SSHClient ssh = new SSHClient();
final File khFile = new File(OpenSSHKnownHosts.detectSSHDir(), "known_hosts");
ssh.addHostKeyVerifier(new ConsoleKnownHostsVerifier(khFile, System.console()));
ssh.connect("localhost");
try {
ssh.authPublickey(System.getProperty("user.name"));
final Session session = ssh.startSession();
try {
session.allocateDefaultPTY();
final Shell shell = session.startShell();
new StreamCopier(shell.getInputStream(), System.out, LoggerFactory.DEFAULT)
.bufSize(shell.getLocalMaxPacketSize())
.spawn("stdout");
new StreamCopier(shell.getErrorStream(), System.err, LoggerFactory.DEFAULT)
.bufSize(shell.getLocalMaxPacketSize())
.spawn("stderr");
new StreamCopier(System.in, shell.getOutputStream(), LoggerFactory.DEFAULT)
.bufSize(shell.getRemoteMaxPacketSize())
.copy();
} finally {
session.close();
}
} finally {
ssh.disconnect();
}
}
}
在這個例子中,使用了會話的 startShell 來啟動一個命令行,而不像我們的第一個例子那樣創建會話。在此之后,使用 SSHJ 提供的 StreamCopier,進行遠程命令行輸出流的獲取,以及本地輸入流的上傳,從而完成了一個實時交互的 SSH 命令行。
SSH協議與SSHJ庫
總結
SSHJ 作為一個使用 Java 語言實現的 SSH 庫,其對于 SSH 協議的實現十分全面,包含的特性眾多,在其所提供的 SSH 實現的比較中,SSHJ 對于協議和算法的實現覆蓋程度很高,是實現 SSH 協議相關代碼邏輯和應用的優秀選擇。
SSHJ 項目代碼質量高,歷經數年的開發,項目一直處于活躍的開發和維護狀態;項目代碼量不大,代碼結構設計較好,且提供了豐富的使用例子,值得有興趣的開發者進行更進一步的學習研究和開源貢獻。






