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

Apache Mina SSHD认证失败:No more authentication methods available问题排查

Apache Mina SSHD私钥认证失败问题排查(对比JSch可行实现)

问题背景

在Spring Boot微服务中,使用Apache Mina SSHD库实现SSH私钥认证时遭遇失败,报错如下:

org.apache.sshd.common.SshException: No more authentication methods available                                                                                                             org.apache.sshd.common.future.AbstractSshFuture.verifyResult(AbstractSshFuture.java:141) ~[sshd-common-2.14.0.jar:2.14.0]                                                          org.apache.sshd.client.future.DefaultAuthFuture.verify(DefaultAuthFuture.java:56) ~[sshd-core-2.14.0.jar:2.14.0]                                                                    org.apache.sshd.client.future.DefaultAuthFuture.verify(DefaultAuthFuture.java:35) ~[sshd-core-2.14.0.jar:2.14.0]                                                                   org.apache.sshd.common.future.VerifiableFuture.verify(VerifiableFuture.java:43) ~[sshd-common-2.14.0.jar:2.14.0]

错误发生在session.auth().verify(ftpTimeOutInSeconds * 1000L);行。

对比之下,基于JSch的实现可正常通过私钥连接,相关代码分别如下:

Apache Mina SSHD 失败代码

try (SshClient sshClient = SshClient.setUpDefaultClient()) {
    sshClient.start();

    try (ClientSession session =
            sshClient.connect(user, host, port).verify(ftpTimeOutInSeconds * 1000L).getSession()) {
   
            Path privateKeyPath = Paths.get(System.getProperty("java.io.tmpdir"), "key.pem");
            FileKeyPairProvider fileKeyPairProvider = new FileKeyPairProvider(privateKeyPath);
            Iterable<KeyPair> keyPairs = fileKeyPairProvider.loadKeys(session);


            if (keyPairs.iterator().hasNext()) {
                session.addPublicKeyIdentity(keyPairs.iterator().next());
            }
        

        session.auth().verify(ftpTimeOutInSeconds * 1000L);
        LOGGER.info("Session authenticated successfully!");
    }
} catch (IOException e) {
    LOGGER.error("I/O Exception occurred: " + e.getMessage());
} catch (Exception e) {
    LOGGER.error("General Exception occurred: " + e.getMessage());
}

JSch 可行代码

JSch jsch = new JSch();
Session session1 = null;

try {
  
    Path privateKeyPath = Paths.get(System.getProperty("java.io.tmpdir"), "key.pem");
    jsch.addIdentity(privateKeyPath.toString());

    session1 = jsch.getSession(user, host, port);
    session1.setTimeout(ftpTimeOutInSeconds * 1000);
    java.util.Properties config = new java.util.Properties();
    config.put("StrictHostKeyChecking", "no");
    session1.setConfig(config);

    session1.connect();
    LOGGER.info("Connected and authenticated successfully!");
} catch (JSchException e) {
    LOGGER.error("JSch Exception occurred: " + e.getMessage());
} finally {
    if (session1 != null && session1.isConnected()) {
        session1.disconnect();
    }
}

问题原因分析

  1. 主机密钥检查差异:JSch中显式禁用了StrictHostKeyChecking,但SSHD默认启用该检查,可能在认证前就阻断了连接
  2. 私钥认证流程问题:原SSHD代码仅添加公钥身份,但未明确触发公钥认证流程,或私钥加载方式未被服务器认可
  3. 认证方法优先级问题:SSHD默认会尝试多种认证方法(如密码、公钥等),若其他方法失败后才尝试公钥,可能已耗尽服务器允许的认证次数

解决方法

1. 对齐主机密钥检查配置

在SSHD客户端初始化时添加禁用主机密钥检查的配置,和JSch行为保持一致:

sshClient.setConfig(ClientFactoryManager.STRICT_HOST_KEY_CHECKING, "no");

2. 简化并明确公钥认证流程

替换原私钥加载代码,使用SSHD提供的authPublicKey方法直接触发公钥认证,避免手动添加身份的不确定性:

try (SshClient sshClient = SshClient.setUpDefaultClient()) {
    // 禁用主机密钥检查
    sshClient.setConfig(ClientFactoryManager.STRICT_HOST_KEY_CHECKING, "no");
    sshClient.start();

    try (ClientSession session = sshClient.connect(user, host, port)
            .verify(ftpTimeOutInSeconds * 1000L)
            .getSession()) {
        Path privateKeyPath = Paths.get(System.getProperty("java.io.tmpdir"), "key.pem");
        
        // 直接调用公钥认证方法,指定用户和私钥路径
        boolean authSuccess = session.authPublicKey(user, privateKeyPath)
                .verify(ftpTimeOutInSeconds * 1000L)
                .isSuccess();
        
        if (authSuccess) {
            LOGGER.info("Session authenticated successfully!");
        } else {
            LOGGER.error("Authentication failed: no valid methods succeeded");
        }
    }
} catch (IOException e) {
    LOGGER.error("I/O Exception occurred: " + e.getMessage());
} catch (Exception e) {
    LOGGER.error("General Exception occurred: " + e.getMessage());
}

3. 处理带密码的私钥(若适用)

如果私钥受密码保护,需在认证时指定密码:

// 假设privateKeyPassword是私钥的密码字符串
boolean authSuccess = session.authPublicKey(user, privateKeyPath, privateKeyPassword)
        .verify(ftpTimeOutInSeconds * 1000L)
        .isSuccess();

调试建议

  • 开启DEBUG日志:在Spring Boot的application.yml或application.properties中添加配置,查看SSHD认证的详细交互:
    logging.level.org.apache.sshd=DEBUG
    
    日志会显示服务器支持的认证方法、本地发送的认证尝试、每一步的结果,帮助定位具体失败环节。
  • 检查私钥格式:确保私钥是标准PEM格式,若为OpenSSH新格式(开头为-----BEGIN OPENSSH PRIVATE KEY-----),可转换为PEM格式后重试。
  • 强制指定认证方法:限制SSHD仅尝试公钥认证,避免其他无关方法干扰:
    session.setPreferredAuthentications("publickey");
    
  • 抓包对比流程:使用Wireshark抓取JSch和SSHD的SSH连接报文,对比两者在认证阶段的请求差异,定位哪一步不符合服务器要求。

内容的提问来源于stack exchange,提问作者Yashasvi Raj Pant

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.17 14:40:12