首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

Java 實現 SSH 協議客戶端認證:從協議原理到密碼/公鑰/鍵盤交互實戰

     編輯:Java 相關知識

SSH 協議架構回顧

SSH(Secure Shell Protocol)工作在 TCP/IP 應用層與傳輸層,提供加密傳輸通道。其核心分層:

  • 傳輸層協議:數據加密、壓縮、服務端認證、完整性校驗(對稱加密、公鑰算法、MAC)。
  • 用戶認證協議:驗證客戶端身份(密碼、公鑰、鍵盤交互等)。
  • 連接協議:會話管理、遠程命令執行、端口轉發。

SSH 協議構成

認證流程核心交互

  1. 客戶端發起 TCP 連接,獲取會話句柄(Session)。
  2. 協商加密算法、壓縮算法、服務端主機金鑰算法。
  3. 發送 SSH_MSG_SERVICE_REQUEST 請求 ssh-userauth 服務。
  4. 客戶端發起認證請求(SSH_MSG_USERAUTH_REQUEST),攜帶用戶名、服務名、認證方法名。
  5. 服務端驗證,返回成功(52)、失敗(51)或繼續認證列表。
  6. 雙方達成一致,建立安全通道,進入連接協議階段。

SSH 登錄工作流程

Java 客戶端架構設計

模塊化分層:

  • TransportManager:處理傳輸層加解密、壓縮、數據包收發(委託 TransportControl 選擇算法)。
  • Connect:建立連接、初始化傳輸層、創建安全通道。
  • AuthManager:協調認證流程,根據服務端支持列表選擇具體認證實現類(AuthRequestByPassword、AuthRequestByPublicKey 等)。

Authentication 類圖 認證協議流程圖

三大主流認證方式的 Java 實現細節

1. None 認證:探測服務端支持列表

發送 SSH_MSG_USERAUTH_REQUEST,方法名為 none。服務端拒絕並返回支持的方法列表(如 publickey,gssapi-with-mic,password)。

public class AuthRequestByNone {
    public byte[] getRequestMessage() {
        ProcessTypes type = new ProcessTypes();
        type.asByte(SSH_MSG_USERAUTH_REQUEST);
        type.asString(userName);
        type.asString(serviceName);
        type.asString(SSH_NONE_AUTHENTICATION_METHOD);
        return type.getBytes();
    }
}

解析失敗響應(代碼 51)提取 name-list 字段,獲取可用認證方法。

2. 密碼認證:最直接但需防暴力破解

請求格式:

byte SSH_MSG_USERAUTH_REQUEST
string userName
string serviceName
string "password"
boolean FALSE
string plaintext password (UTF-8)

實現關鍵:

public boolean passwordAuthentication(String user, String pass) throws IOException {
    initialize(user); // 協商算法、獲取支持列表
    if (!verifyAuthenticatonMethods(SSH_PASSWORD_AUTHENTICATION_METHOD)) return false;
    
    AuthRequestByPassword req = new AuthRequestByPassword(serviceName, user, pass);
    transportManager.sendMessage(req.getRequestMessage());
    byte[] msg = getMessage();
    return !isRequestFailed(msg); // 52 成功,51 失敗
}

安全建議:生產環境禁用密碼認證,改用公鑰;必須啟用 Fail2ban 限制嘗試次數。

3. 公鑰認證:無密碼登錄的標準選擇

流程:客戶端用私鑰簽名會話 ID + 請求數據,服務端用授權公鑰驗證簽名。Java 實現需:

  • 加載私鑰(PEM/PKCS8 格式),支持加密私鑰解密。
  • 構造簽名數據:sessionId || SSH_MSG_USERAUTH_REQUEST || userName || serviceName || "publickey" || TRUE || keyAlgorithm || publicKeyBlob。
  • 發送含簽名的請求,服務端驗證通過返回成功。

代碼結構類似密碼認證,核心差異在 getRequestMessage() 中組裝簽名 Blob。

4. 鍵盤交互認證:適用於多因子/挑戰-響應場景

服務端發送提示(SSH_MSG_USERAUTH_INFO_REQUEST),客戶端交互式輸入響應。常見於:

  • 雙因子認證(密碼 + 動態碼)。
  • 首次登錄強制修改密碼。
  • PAM 模塊驅動的複雜認證流程。

環境準備與調試技巧

  • 服務端:Linux + OpenSSH,確保 sshd 運行、防火牆放行 22 端口。
  • 客戶端:Windows + PuTTY 驗證連通性,排除網絡/配置問題。
  • 數據包抓取:Wireshark 過濾 ssh 協議,分析握手、認證交互細節。
  • 常見錯誤:bad service request(服務名錯誤)、Permission denied(認證方法不支持或憑證錯誤)。

總結與擴展方向

本文以密碼認證為例,完整展示了 SSH 用戶認證層的 Java 實現邏輯。關鍵點:

  1. 嚴格遵循 RFC 4252/4253/4254 協議規範構造二進制數據包。
  2. 利用 TransportManager 統一處理加解密、壓縮、序列化。
  3. AuthManager 解耦具體認證方式,便於擴展新方法(如硬件 Token、生物識別)。

後續可深入:公鑰認證完整代碼、GSSAPI/Kerberos 整合、SSH Agent 轉發、連接層通道管理(Shell、Exec、Subsystem、Port Forwarding)。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved