使用Apache SSHD通过网关隧道连接node1服务器失败求助
问题分析与解决方案
你的报错核心是客户端不支持服务器(node1)使用的ssh-ed25519主机密钥算法,而非密钥交换(KeyExchange)算法不匹配——你之前调整KeyExchangeFactories的方向错了。
报错信息明确显示:
Error: Unable to negotiate key exchange for server host key algorithms (client: ... / server: ssh-ed25519)
注意这里的server host key algorithms是指服务器用于身份验证的主机密钥类型,不是密钥交换算法。你的Apache SSHD客户端默认没启用ed25519主机密钥支持,导致无法和只支持ssh-ed25519的node1协商。
解决步骤
1. 启用ed25519主机密钥支持
Apache SSHD 2.11.0已经支持ed25519,但默认未将其加入启用的主机密钥算法列表,需要显式配置。
2. 修改客户端配置
在初始化SshClient时,添加ed25519相关配置,替换你原本的KeyExchangeFactories配置部分:
SshClient client = SshClient.setUpDefaultClient(); // 启用ed25519主机密钥算法,同时保留其他常用算法 client.setHostKeyAlgorithms("ssh-ed25519", "ecdsa-sha2-nistp256", "rsa-sha2-256", "ssh-rsa"); // 添加ed25519密钥提供者 client.setKeyPairProvider(KeyPairProvider.ED25519); // 保留原有其他配置 client.setCompressionFactoriesNames(BuiltinCompressions.Constants.NONE); client.setCipherFactoriesNames("aes256-gcm@openssh.com","aes128-gcm@openssh.com","aes256-ctr","aes192-ctr","aes128-ctr"); client.setMacFactoriesNames("hmac-sha2-256-etm@openssh.com","hmac-sha2-512-etm@openssh.com","hmac-sha2-256","hmac-sha2-512");
3. 额外注意事项
- 从网关获取的私钥需确保已被node1信任(即该私钥的公钥已添加到node1的
~/.ssh/authorized_keys文件中)。 - 如果node1使用的是ed25519类型私钥,当前版本的sshd-core已支持加载,无需额外依赖。
修改后的完整代码示例
package org.example; import org.apache.sshd.client.SshClient; import org.apache.sshd.client.channel.ChannelExec; import org.apache.sshd.client.channel.ClientChannelEvent; import org.apache.sshd.client.session.ClientSession; import org.apache.sshd.common.compression.BuiltinCompressions; import org.apache.sshd.common.config.keys.FilePasswordProvider; import org.apache.sshd.common.keyprovider.FileKeyPairProvider; import org.apache.sshd.common.keyprovider.KeyPairProvider; import org.apache.sshd.common.util.net.SshdSocketAddress; import java.io.ByteArrayOutputStream; import java.io.File; import java.nio.file.Files; import java.security.KeyPair; import java.util.EnumSet; import java.util.List; import java.util.ArrayList; import java.util.stream.Collectors; public class RemoteCommandExecutor { public static void main(String[] args) throws Exception { String gatewayHost = "host ip"; String gatewayUser = "username"; String gatewayPassword = "password"; int localPort = 12345; // 选择未使用的端口 int remotePort = 22; // node1的SSH端口 String node1Host = "ixxxxxx017"; String node1User = "username"; String passphrase = "password"; List<String> commands = List.of("hostname", "cd /home/", "ls -lrt"); String privateKeyPath = "/home/"+gatewayUser+"/.ssh/id_rsa"; try { SshClient client = SshClient.setUpDefaultClient(); // 启用ed25519主机密钥算法 client.setHostKeyAlgorithms("ssh-ed25519", "ecdsa-sha2-nistp256", "rsa-sha2-256", "ssh-rsa"); // 添加ed25519密钥提供者 client.setKeyPairProvider(KeyPairProvider.ED25519); // 原有配置 client.setCompressionFactoriesNames(BuiltinCompressions.Constants.NONE); client.setCipherFactoriesNames("aes256-gcm@openssh.com","aes128-gcm@openssh.com","aes256-ctr","aes192-ctr","aes128-ctr"); client.setMacFactoriesNames("hmac-sha2-256-etm@openssh.com","hmac-sha2-512-etm@openssh.com","hmac-sha2-256","hmac-sha2-512"); client.start(); // 连接网关 try (ClientSession gatewaySession = client.connect(gatewayUser, gatewayHost, 22) .verify().getSession()) { gatewaySession.addPasswordIdentity(gatewayPassword); gatewaySession.auth().verify(); String testOutput = executeCommand(gatewaySession, "ls -l"); System.out.println("网关测试输出:" + testOutput); // 建立本地端口转发 gatewaySession.startLocalPortForwarding( new SshdSocketAddress("localhost", localPort), new SshdSocketAddress(node1Host, remotePort) ); // 通过隧道连接node1 try (ClientSession node1Session = client.connect(node1User, "localhost", localPort) .verify().getSession()) { // 从网关获取私钥内容 String privateKeyContents = executeCommand(gatewaySession, "cat " + privateKeyPath); File tempPrivateKeyFile = File.createTempFile("temp-private-key", ".pem"); Files.write(tempPrivateKeyFile.toPath(), privateKeyContents.getBytes()); // 加载私钥 FileKeyPairProvider keyPairProvider = new FileKeyPairProvider(List.of(tempPrivateKeyFile.toPath())); keyPairProvider.setPasswordFinder(FilePasswordProvider.of(passphrase)); Iterable<KeyPair> keyPairs = keyPairProvider.loadKeys(node1Session); KeyPair keyPair = keyPairs.iterator().next(); node1Session.addPublicKeyIdentity(keyPair); node1Session.auth().verify(); // 执行命令 List<String> results = new ArrayList<>(); for (String cmd : commands) { String output = executeCommand(node1Session, cmd); results.add(output); } String combinedOutput = results.stream().collect(Collectors.joining("\n---\n")); System.out.println("node1命令输出:\n" + combinedOutput); } } finally { client.stop(); client.close(); } } catch (Exception e) { System.err.println("错误:" + e.getMessage()); e.printStackTrace(); } } private static String executeCommand(ClientSession session, String command) throws Exception { ChannelExec channelExec = session.createExecChannel(command); ByteArrayOutputStream out = new ByteArrayOutputStream(); ByteArrayOutputStream err = new ByteArrayOutputStream(); channelExec.setOut(out); channelExec.setErr(err); channelExec.open(); channelExec.waitFor(EnumSet.of(ClientChannelEvent.CLOSED), 0); String output = new String(out.toByteArray()); String error = new String(err.toByteArray()); if (!error.isEmpty() && !error.contains("cannot find name for group ID")) { throw new Exception("命令执行错误:" + error); } return output; } }
Maven依赖(无需修改)
<dependencies> <dependency> <groupId>org.apache.sshd</groupId> <artifactId>sshd-core</artifactId> <version>2.11.0</version> </dependency> <dependency> <groupId>org.apache.sshd</groupId> <artifactId>sshd-sftp</artifactId> <version>2.11.0</version> </dependency> <dependency> <groupId>org.apache.sshd</groupId> <artifactId>sshd-common</artifactId> <version>2.11.0</version> </dependency> </dependencies>
内容的提问来源于stack exchange,提问作者Ravi
相关产品推荐
相关产品推荐

