You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用Java获取远程SSH服务器的主机密钥

通过Java获取远程SSH主机密钥的实现方案

方案1:JSch原生实现(最优纯Java方案)

你之前调研的JSch完全可以满足需求,多数开发者没找到对应实现的核心原因是误以为需要完成完整SSH登录流程,实际上只要走完SSH密钥交换阶段,在身份认证前就能拿到主机公钥,无需提供合法登录凭据。

核心实现逻辑如下:

  • 自定义HostKeyRepository实现,跳过本地known_hosts校验,强制触发主机密钥回调
  • 创建测试会话触发SSH握手,无需填入有效用户名、密码等认证信息
  • 握手完成后直接从HostKeyRepository中提取拿到的主机密钥,终止会话即可

代码示例:

import com.jcraft.jsch.HostKey;
import com.jcraft.jsch.HostKeyRepository;
import com.jcraft.jsch.JSch;
import com.jcraft.jsch.Session;
import com.jcraft.jsch.UserInfo;

public class SshHostKeyFetcher {
    public static HostKey fetchHostKey(String hostname, int port) throws Exception {
        JSch jsch = new JSch();
        // 替换默认的known_hosts仓库,跳过本地缓存校验
        jsch.setHostKeyRepository(new HostKeyRepository() {
            private HostKey fetchedKey;
            @Override
            public int check(String host, byte[] key) {
                try {
                    fetchedKey = new HostKey(host, key);
                } catch (Exception e) {
                    // 忽略构造异常
                }
                // 直接返回未知主机,无需继续校验
                return UNKNOWN;
            }
            @Override public void add(HostKey hostkey, UserInfo ui) {}
            @Override public void remove(String host, String type) {}
            @Override public void remove(String host, String type, byte[] key) {}
            @Override public String getKnownHostsRepositoryID() { return null; }
            @Override public HostKey[] getHostKey() { return new HostKey[0]; }
            @Override public HostKey[] getHostKey(String host, String type) {
                return fetchedKey != null ? new HostKey[]{fetchedKey} : null;
            }
        });

        Session session = jsch.getSession("dummy", hostname, port);
        session.setConfig("StrictHostKeyChecking", "yes");
        session.setConfig("ConnectTimeout", "5000");
        // 传入空的用户信息,不会实际完成认证
        session.setUserInfo(new UserInfo() {
            @Override public String getPassphrase() { return null; }
            @Override public String getPassword() { return null; }
            @Override public boolean promptPassword(String message) { return false; }
            @Override public boolean promptPassphrase(String message) { return false; }
            @Override public boolean promptYesNo(String message) { return false; }
            @Override public void showMessage(String message) {}
        });

        try {
            session.connect();
        } catch (Exception ignored) {
            // 连接必然因为认证失败抛出异常,此时已经拿到主机密钥
            HostKey[] keys = jsch.getHostKeyRepository().getHostKey(hostname, null);
            if (keys != null && keys.length > 0) {
                return keys[0];
            }
            throw new Exception("未获取到目标主机的SSH密钥");
        } finally {
            if (session.isConnected()) {
                session.disconnect();
            }
        }
    }

    public static void main(String[] args) throws Exception {
        HostKey key = fetchHostKey("你的主机地址", 22);
        // 密钥类型,如ssh-rsa、ecdsa-sha2-nistp256等
        System.out.println("密钥类型:" + key.getType());
        // MD5格式的密钥指纹
        System.out.println("密钥指纹:" + key.getFingerPrint(new JSch()));
        // Base64编码的原始公钥内容
        System.out.println("公钥内容:" + key.getKey());
    }
}

方案2:Apache MINA SSHD实现(备选纯Java方案)

如果不想使用JSch,也可以选择Apache MINA SSHD库实现,核心逻辑和JSch类似,在SSH握手阶段的主机密钥校验回调中捕获公钥后直接终止连接即可,无需完成登录。
Maven依赖示例:

<dependency>
    <groupId>org.apache.sshd</groupId>
    <artifactId>sshd-core</artifactId>
    <version>2.10.0</version>
    <scope>compile</scope>
</dependency>

方案3:调用系统ssh-keyscan命令(最终备选)

仅当纯Java方案不满足你的场景时再使用该方案,需要注意Windows环境需要预先安装OpenSSH客户端或者Git Bash等支持ssh-keyscan命令的环境,代码示例:

public static String fetchHostKeyByCmd(String hostname, int port) throws Exception {
    Process process = new ProcessBuilder("ssh-keyscan", "-p", String.valueOf(port), hostname)
            .redirectErrorStream(true)
            .start();
    String output = new String(process.getInputStream().readAllBytes());
    int exitCode = process.waitFor();
    if (exitCode != 0) {
        throw new Exception("ssh-keyscan执行失败,退出码:" + exitCode);
    }
    return output.lines()
            .filter(line -> !line.startsWith("#") && !line.isBlank())
            .findFirst()
            .orElseThrow(() -> new Exception("未获取到目标主机公钥"));
}

内容的提问来源于stack exchange,提问作者c_anirudh

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.05 20:06:03