如何使用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
相关产品推荐
相关产品推荐

