使用sshj包报错“TransportException [HOST_KEY_NOT_VERIFIABLE] Could not verify `ssh-ed25519` host key”

异常

使用 sshj 包远程连接服务器报错:

Exception in thread "main" net.schmizz.sshj.transport.TransportException: [HOST_KEY_NOT_VERIFIABLE] Could not verify `ssh-ed25519` host key with fingerprint `95:be:4e:db:cd:69:c3:68:b2:bc:6d:54:93:81:0a:56` for `124.75.35.139` on port 22
	at net.schmizz.sshj.transport.KeyExchanger.verifyHost(KeyExchanger.java:220)
	at net.schmizz.sshj.transport.KeyExchanger.handle(KeyExchanger.java:390)
	at net.schmizz.sshj.transport.TransportImpl.handle(TransportImpl.java:474)
	at net.schmizz.sshj.transport.Decoder.decode(Decoder.java:113)
	at net.schmizz.sshj.transport.Decoder.received(Decoder.java:200)
	at net.schmizz.sshj.transport.Reader.run(Reader.java:60)

错误代码

代码如下:

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.IOException;
import java.util.concurrent.TimeUnit;

public class Demo {
    public static void main(String[] args) throws IOException {
        // 创建 SSH 客户端
        final SSHClient ssh = new SSHClient();
        ssh.loadKnownHosts();
        // 传入主机名或IP地址建立连接
        ssh.connect("124.75.35.139");
        Session session = null;
        try {
            // 通过用户名和密码进行验证
            ssh.authPassword("root", "root");
            // 开启会话
            session = ssh.startSession();
            // 执行命令
            final Command cmd = session.exec("ls ~");
            // 获取命令的执行结果
            String response = IOUtils.readFully(cmd.getInputStream()).toString();
            // 打印命令的执行结果
            System.out.println(response);
            cmd.join(50, TimeUnit.SECONDS);
            System.out.println("exit status: " + cmd.getExitStatus());
        } finally {
            try {
                // 关闭会话
                if (session != null) {
                    session.close();
                }
            } catch (IOException e) {
                // Do Nothing
            }
            // 关闭连接
            ssh.disconnect();
        }
    }
}

注意,其中 IP 地址、用户名和密码需要变成你自己的,其他都可以直接使用进行测试。而这里使用的 ssj 的版本如下:

        <dependency>
            <groupId>com.hierynomus</groupId>
            <artifactId>sshj</artifactId>
            <version>0.32.0</version>
        </dependency>

原因

主机验证失败。

参考资料:

解决

根据查找的资料,有如下三种解决方式:

  • 第一种,将报错信息中的 key 设置到 addHostKeyVerifier 方法,注意在 connect 方法之前。但不推荐。如 ssh.addHostKeyVerifier("95:be:4e:db:cd:69:c3:68:b2:bc:6d:54:93:81:0a:56");
  • 第二种,在 try...catch 语句中捕获该异常,从报出的异常提示信息中提取出 key,可以通过正则表达式来提取,然后将提取到的 key 设置到 addHostKeyVerifier 方法,即尝试了两次建立连接。
  • 第三种,通过 ssh.addHostKeyVerifier(new PromiscuousVerifier()); 的方式跳过主机密钥验证。

正确代码

第一种方式的代码如下, 注意 key 是我们手动从异常信息中复制粘贴出来的,如果换台远程主机连接,那么这个 key 又会不一样,所以不推荐该种方式。

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.IOException;
import java.util.concurrent.TimeUnit;

public class Demo {
    public static void main(String[] args) throws IOException {
        // 创建 SSH 客户端
        final SSHClient ssh = new SSHClient();
        ssh.loadKnownHosts();
        // 直接设置 key
        ssh.addHostKeyVerifier("95:be:4e:db:cd:69:c3:68:b2:bc:6d:54:93:81:0a:56");
        // 传入主机名或IP地址建立连接
        ssh.connect("124.75.35.139");
        Session session = null;
        try {
            // 通过用户名和密码进行验证
            ssh.authPassword("root", "root");
            // 开启会话
            session = ssh.startSession();
            // 执行命令
            final Command cmd = session.exec("ls ~");
            // 获取命令的执行结果
            String response = IOUtils.readFully(cmd.getInputStream()).toString();
            // 打印命令的执行结果
            System.out.println(response);
            cmd.join(50, TimeUnit.SECONDS);
            System.out.println("exit status: " + cmd.getExitStatus());
        } finally {
            try {
                // 关闭会话
                if (session != null) {
                    session.close();
                }
            } catch (IOException e) {
                // Do Nothing
            }
            // 关闭连接
            ssh.disconnect();
        }
    }
}

第二种方式,通过 try...catch 捕捉到该异常后,从异常信息种自动提取 key,然后再次发送请求建立连接。代码如下:

import net.schmizz.sshj.SSHClient;
import net.schmizz.sshj.common.DisconnectReason;
import net.schmizz.sshj.common.IOUtils;
import net.schmizz.sshj.connection.channel.direct.Session;
import net.schmizz.sshj.connection.channel.direct.Session.Command;
import net.schmizz.sshj.transport.TransportException;

import java.io.IOException;
import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class Demo {
    public static void main(String[] args) throws IOException {
        // 创建 SSH 客户端
        SSHClient ssh = new SSHClient();
        ssh.loadKnownHosts();
        // 传入主机名或IP地址建立连接
        try {
            ssh.connect("124.75.35.139");
        } catch (TransportException e) {
            // 如果抛出该异常,则自动提取 key,再次尝试建立连接
            if (e.getDisconnectReason() == DisconnectReason.HOST_KEY_NOT_VERIFIABLE) {
                ssh = new SSHClient();
                String msg = e.getMessage();
                Matcher matcher = Pattern.compile("`((\\w{2}:?){16})`").matcher(msg);
                if (matcher.find()) {
                    String vc = matcher.group(1);
                    ssh.addHostKeyVerifier(vc);
                    ssh.connect("124.75.35.139");
                }
            } else {
                e.printStackTrace();
            }
        }
        Session session = null;
        try {
            // 通过用户名和密码进行验证
            ssh.authPassword("root", "root");
            // 开启会话
            session = ssh.startSession();
            // 执行命令
            final Command cmd = session.exec("ls ~");
            // 获取命令的执行结果
            String response = IOUtils.readFully(cmd.getInputStream()).toString();
            // 打印命令的执行结果
            System.out.println(response);
            cmd.join(50, TimeUnit.SECONDS);
            System.out.println("exit status: " + cmd.getExitStatus());
        } finally {
            try {
                // 关闭会话
                if (session != null) {
                    session.close();
                }
            } catch (IOException e) {
                // Do Nothing
            }
            // 关闭连接
            ssh.disconnect();
        }
    }
}

第三种方式是直接跳过密钥验证。代码如下:

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 net.schmizz.sshj.transport.verification.PromiscuousVerifier;

import java.io.IOException;
import java.util.concurrent.TimeUnit;

public class Demo {
    public static void main(String[] args) throws IOException {
        // 创建 SSH 客户端
        final SSHClient ssh = new SSHClient();
        ssh.loadKnownHosts();
        // 跳过密钥验证
        ssh.addHostKeyVerifier(new PromiscuousVerifier());
        // 传入主机名或IP地址建立连接
        ssh.connect("124.75.35.139");
        Session session = null;
        try {
            // 通过用户名和密码进行验证
            ssh.authPassword("root", "root");
            // 开启会话
            session = ssh.startSession();
            // 执行命令
            final Command cmd = session.exec("ls ~");
            // 获取命令的执行结果
            String response = IOUtils.readFully(cmd.getInputStream()).toString();
            // 打印命令的执行结果
            System.out.println(response);
            cmd.join(50, TimeUnit.SECONDS);
            System.out.println("exit status: " + cmd.getExitStatus());
        } finally {
            try {
                // 关闭会话
                if (session != null) {
                    session.close();
                }
            } catch (IOException e) {
                // Do Nothing
            }
            // 关闭连接
            ssh.disconnect();
        }
    }
}
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值