<dependency><groupId>org.apache.sshd</groupId><artifactId>sshd-core</artifactId><version>2.11.0</version></dependency><!-- 如果需要 OpenSSH 格式支持 --><dependency><groupId>org.apache.sshd</groupId><artifactId>sshd-common</artifactId><version>2.11.0</version></dependency>
密码登录
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 java.util.EnumSet;public class SshConnectWithPassword {public static void main(String[] args) {String host = "192.168.56.122"; // 服务器地址String username = "root"; // 用户名String password = "root"; // 密码int port = 22; // SSH端口,默认22SshClient client = SshClient.setUpDefaultClient();try {client.start();// 连接到服务器并获取会话ClientSession session = client.connect(username, host, port).verify(5000) // 连接超时时间.getSession();// 添加密码认证session.addPasswordIdentity(password);// 进行认证if (session.auth().verify(5000).isSuccess()) { // 认证超时时间System.out.println("SSH 密码认证成功!");// 执行命令示例try (ChannelExec channel = session.createExecChannel("ls -l")) {channel.setOut(System.out);channel.setErr(System.err);channel.open().verify(5000); // 通道打开超时时间channel.waitFor(EnumSet.of(ClientChannelEvent.CLOSED), 0);}} else {System.err.println("SSH 认证失败");}session.close();} catch (Exception e) {e.printStackTrace();} finally {client.stop();}}
}
证书登录
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.nio.file.Paths;
import java.security.KeyPair;
import java.util.Collection;
import java.util.EnumSet;
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.keyverifier.AcceptAllServerKeyVerifier;
import org.apache.sshd.client.session.ClientSession;
import org.apache.sshd.common.config.keys.loader.KeyPairResourceLoader;
import org.apache.sshd.common.util.security.SecurityUtils;public class SshConnectWithPublicKey {private SshClient client;private ClientSession session;
/*** 连接服务器 - 支持 OpenSSH 新格式私钥*/public boolean connect(String host, String username, String privateKeyPath, String passphrase, int port) {try {// 创建 SSH 客户端client = SshClient.setUpDefaultClient();
client.setServerKeyVerifier(AcceptAllServerKeyVerifier.INSTANCE);client.start();// 加载密钥对 - 支持 OpenSSH 新格式KeyPairResourceLoader loader = SecurityUtils.getKeyPairResourceParser();Collection<KeyPair> keys;
if (passphrase != null && !passphrase.isEmpty()) {keys = loader.loadKeyPairs(null, Paths.get(privateKeyPath), (session, resource, index) -> passphrase);} else {keys = loader.loadKeyPairs(null, Paths.get(privateKeyPath), null);}if (keys.isEmpty()) {System.err.println("无法加载私钥文件");return false;}KeyPair keyPair = keys.iterator().next();// 创建会话并认证
session = client.connect(username, host, port).verify(5000).getSession();session.addPublicKeyIdentity(keyPair);session.auth().verify(5000);System.out.println("SSH 连接成功 (使用 OpenSSH 新格式密钥)!");return true;} catch (Exception e) {System.err.println("连接失败: ");e.printStackTrace();return false;}}/*** 执行命令*/public String executeCommand(String command) throws Exception {if (session == null || !session.isAuthenticated()) {throw new IllegalStateException("会话未认证");}try (ChannelExec channel = session.createExecChannel(command)) {// 收集标准输出和错误输出// 使用 ByteArrayOutputStream 捕获输出ByteArrayOutputStream outputStream = new ByteArrayOutputStream();ByteArrayOutputStream errorStream = new ByteArrayOutputStream();channel.setOut(outputStream);channel.setErr(errorStream);channel.open().verify(5000); // 通道打开超时时间channel.waitFor(EnumSet.of(ClientChannelEvent.CLOSED), 0); // 等待命令执行完毕// 获取命令退出状态码,通常 0 表示成功Integer exitStatus = channel.getExitStatus();System.out.println("命令输出: " + new String(outputStream.toByteArray()));if (errorStream.toByteArray().length > 0) {System.err.println("错误信息: " + new String(errorStream.toByteArray()));}System.out.println("退出状态码: " + exitStatus);return new String(outputStream.toByteArray());}}/*** 断开连接* * @throws IOException*/public void disconnect() throws IOException {if (session != null) {session.close();}if (client != null) {client.stop();}}// 生成秘钥(可以不输入密码全部为空):ssh-keygen -t rsa // 拷贝公钥到其他主机:ssh-copy-id -i ~/.ssh/id_rsa.pub root@192.168.56.122public static void main(String[] args) {String host = "192.168.56.122";String username = "root";String privateKeyPath = "d:/id_rsa"; // OpenSSH 新格式int port = 22;
// 方案1: 使用 Apache MINA SSHD(推荐)System.out.println("=== 方案1: 使用 Apache MINA SSHD ===");SshConnectWithPublicKey sshConnectWithPublicKey = new SshConnectWithPublicKey();try {if (sshConnectWithPublicKey.connect(host, username, privateKeyPath, null, port)) { String result = sshConnectWithPublicKey.executeCommand("uname -a");System.out.println("系统信息----: " + result);System.out.println("-------------------");result = sshConnectWithPublicKey.executeCommand("ls -ltra /");System.out.println("ls结果----: \n" + result);}} catch (Exception e) {System.err.println("方案1失败: " + e.getMessage());} finally {try {sshConnectWithPublicKey.disconnect();} catch (IOException e) {e.printStackTrace();}}}
}
生成秘钥 认证
AutoSSHSetup
import java.nio.file.Paths;
import java.security.KeyPair;public class AutoSSHSetup {/*** 完整的免密登录设置流程*/public static void setupPasswordLogin(String host, String username, String password, int port, String keySavePath, String paasword) {try {// 1. 设置密钥文件路径
// String userHome = System.getProperty("user.home");
// String privateKeyPath = userHome + "/.ssh/id_rsa_java_generated";
// String publicKeyPath = privateKeyPath + ".pub";String privateKeyPath = keySavePath + "id_rsa_java_generated";String publicKeyPath = keySavePath + "id_rsa_java_generated.pub";System.out.println("=== 开始设置 SSH 免密登录 ===");System.out.println("目标服务器: " + username + "@" + host + ":" + port);// 2. 检查是否已存在密钥if (!Paths.get(privateKeyPath).toFile().exists()) {System.out.println("1. 生成新的 SSH 密钥对...");// 生成密钥对KeyPair keyPair = SSHKeyManager.generateKeyPair();// 保存密钥对SSHKeyManager.saveKeyPair( keyPair, privateKeyPath, publicKeyPath, "save private key", paasword);} else {System.out.println("1. 使用现有密钥对: " + privateKeyPath);}// 3. 上传公钥到服务器System.out.println("2. 上传公钥到服务器...");if (SSHKeyManager.uploadPublicKey(host, username, password, publicKeyPath, port)) {System.out.println("✓ 公钥上传成功");} else {System.err.println("✗ 公钥上传失败");return;}// 4. 测试密钥连接System.out.println("3. 测试密钥认证...");if (SSHKeyManager.testKeyConnection(host, username, privateKeyPath, port, paasword)) {System.out.println("✓ 免密登录设置成功!");System.out.println("私钥位置: " + privateKeyPath);System.out.println("公钥位置: " + publicKeyPath);} else {System.err.println("✗ 免密登录测试失败");}
} catch (Exception e) {System.err.println("设置过程中出现错误: " + e.getMessage());e.printStackTrace();}}/*** 完整的免密登录设置流程*/public static void setupPasswordLessLogin(String host, String username, String password, int port, String keySavePath) {try {// 1. 设置密钥文件路径
// String userHome = System.getProperty("user.home");
// String privateKeyPath = userHome + "/.ssh/id_rsa_java_generated";
// String publicKeyPath = privateKeyPath + ".pub";String privateKeyPath = keySavePath + "id_rsa_java_generated";String publicKeyPath = keySavePath + "id_rsa_java_generated.pub";System.out.println("=== 开始设置 SSH 免密登录 ===");System.out.println("目标服务器: " + username + "@" + host + ":" + port);// 2. 检查是否已存在密钥if (!Paths.get(privateKeyPath).toFile().exists()) {System.out.println("1. 生成新的 SSH 密钥对...");// 生成密钥对KeyPair keyPair = SSHKeyManager.generateKeyPair();// 保存密钥对SSHKeyManager.saveKeyPair(keyPair, privateKeyPath, publicKeyPath, "save private key");} else {System.out.println("1. 使用现有密钥对: " + privateKeyPath);}// 3. 上传公钥到服务器System.out.println("2. 上传公钥到服务器...");if (SSHKeyManager.uploadPublicKey(host, username, password, publicKeyPath, port)) {System.out.println("✓ 公钥上传成功");} else {System.err.println("✗ 公钥上传失败");return;}
// 4. 测试密钥连接System.out.println("3. 测试密钥认证...");if (SSHKeyManager.testKeyConnection(host, username, privateKeyPath, port)) {System.out.println("✓ 免密登录设置成功!");System.out.println("私钥位置: " + privateKeyPath);System.out.println("公钥位置: " + publicKeyPath);} else {System.err.println("✗ 免密登录测试失败");}
} catch (Exception e) {System.err.println("设置过程中出现错误: " + e.getMessage());e.printStackTrace();}}/*** 生成密钥对但不自动上传*/public static void generateKeysOnly(String privateKeyPath, String publicKeyPath) {try {System.out.println("=== 生成 SSH 密钥对 ===");KeyPair keyPair = SSHKeyManager.generateKeyPair();SSHKeyManager.saveKeyPair(keyPair, privateKeyPath, publicKeyPath, "save private key");System.out.println("密钥对生成完成:");System.out.println("私钥: " + privateKeyPath);System.out.println("公钥: " + publicKeyPath);} catch (Exception e) {System.err.println("生成密钥对失败: " + e.getMessage());}}
}
SSHKeyExample
import java.security.KeyPair;public class SSHKeyExample {public static void test1() {// 配置参数String host = "192.168.56.123";String username = "root";String password = "root"; // 服务器密码int port = 22;
// 示例1: 完整的免密登录设置System.out.println("=== 示例1: 完整免密登录设置 ===");AutoSSHSetup.setupPasswordLessLogin(host, username, password, port, "d:/");}public static void test2() {// 示例2: 仅生成密钥对System.out.println("\n=== 示例2: 仅生成密钥对 ===");// String userHome = System.getProperty("user.home") + "/.ssh/";String userHome = "d:/";AutoSSHSetup.generateKeysOnly(userHome + "my_custom_key",userHome + "my_custom_key.pub");}
public static void test3() {// 配置参数String host = "192.168.56.122";String username = "root";String password = "root"; // 服务器密码int port = 22;// 示例3: 手动分步操作System.out.println("\n=== 示例3: 手动分步操作 ===");String userHome = "d:/";try {// 生成密钥KeyPair keyPair = SSHKeyManager.generateKeyPair();// String privateKey = userHome + "/.ssh/manual_key";
// String publicKey = privateKey + ".pub";String privateKey = userHome + "manual_key";String publicKey = privateKey + ".pub";SSHKeyManager.saveKeyPair(keyPair, privateKey, publicKey, "save private key");// 上传公钥if (SSHKeyManager.uploadPublicKey(host, username, password, publicKey, port)) {// 测试连接SSHKeyManager.testKeyConnection(host, username, privateKey, port);}} catch (Exception e) {e.printStackTrace();}
}public static void test4() {// 配置参数String host = "192.168.56.123";String username = "root";String password = "root"; // 服务器密码int port = 22;//示例1: 完整的免密登录设置System.out.println("=== 示例1: 完整免密登录设置 ===");AutoSSHSetup.setupPasswordLogin(host, username, password, port, "d:/", "admin123");}public static void main(String[] args) {test1();// String host = "192.168.56.122";// String username = "root";// int port = 22;// SSHKeyManager.testKeyConnection(host, username, "d:/id_rsa_java_generated", port);}}
SSHKeyManager
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.config.keys.KeyUtils;import org.apache.sshd.common.config.keys.loader.KeyPairResourceLoader;
import org.apache.sshd.common.config.keys.writer.openssh.OpenSSHKeyPairResourceWriter;
import org.apache.sshd.common.config.keys.writer.openssh.OpenSSHKeyEncryptionContext;
import org.apache.sshd.common.config.keys.FilePasswordProvider;
import org.apache.sshd.common.util.security.SecurityUtils;
import org.apache.sshd.common.config.keys.PublicKeyEntry;import java.io.*;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.security.KeyPair;
import java.util.Collection;
import java.util.EnumSet;public class SSHKeyManager {/*** 生成新的 RSA 密钥对*/public static KeyPair generateKeyPair() throws Exception {System.out.println("正在生成 RSA 密钥对...");
// return KeyUtils.generateKeyPair(KeyUtils.RSA_ALGORITHM, 2048);return KeyUtils.generateKeyPair("ssh-rsa", 2048);}/*** 保存私钥到文件*/public static void saveKeyPair(KeyPair keyPair, String privateKeyPath, String publicKeyPath, String comment, String password)throws Exception {// 确保目录存在Path privatePath = Paths.get(privateKeyPath);Path publicPath = Paths.get(publicKeyPath);
Files.createDirectories(privatePath.getParent());Files.createDirectories(publicPath.getParent());// 保存私钥
// String privateKeyContent = KeyUtils.getKeyPairResourceParser()
// .encodeKeyPair(keyPair, null); // 无密码保护
OpenSSHKeyPairResourceWriter writer = new OpenSSHKeyPairResourceWriter();ByteArrayOutputStream out = new ByteArrayOutputStream();// 保存加密的私钥OpenSSHKeyEncryptionContext context = new OpenSSHKeyEncryptionContext();context.setPassword(password);
context.setCipherName("AES");context.setCipherType("256");writer.writePrivateKey(keyPair, comment, context, out); // 无密码保护Files.write(privatePath, out.toByteArray(), StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING);
// 设置私钥文件权限为 600if (!privatePath.toFile().setReadable(false, false) || !privatePath.toFile().setWritable(false, false)|| !privatePath.toFile().setReadable(true, true) || !privatePath.toFile().setWritable(true, true)) {
System.err.println("警告: 无法设置私钥文件权限");}// 保存公钥String publicKeyContent = PublicKeyEntry.toString(keyPair.getPublic());Files.write(publicPath, publicKeyContent.getBytes(), StandardOpenOption.CREATE,StandardOpenOption.TRUNCATE_EXISTING);System.out.println("私钥已保存: " + privateKeyPath);System.out.println("公钥已保存: " + publicKeyPath);}/*** 保存密钥对到文件*/public static void saveKeyPair(KeyPair keyPair, String privateKeyPath, String publicKeyPath, String comment)throws Exception {// 确保目录存在Path privatePath = Paths.get(privateKeyPath);Path publicPath = Paths.get(publicKeyPath);Files.createDirectories(privatePath.getParent());Files.createDirectories(publicPath.getParent());
// 保存私钥
// String privateKeyContent = KeyUtils.getKeyPairResourceParser()
// .encodeKeyPair(keyPair, null); // 无密码保护OpenSSHKeyPairResourceWriter writer = new OpenSSHKeyPairResourceWriter();ByteArrayOutputStream out = new ByteArrayOutputStream();writer.writePrivateKey(keyPair, comment, null, out); // 无密码保护Files.write(privatePath, out.toByteArray(), StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING);// 设置私钥文件权限为 600if (!privatePath.toFile().setReadable(false, false) || !privatePath.toFile().setWritable(false, false)|| !privatePath.toFile().setReadable(true, true) || !privatePath.toFile().setWritable(true, true)) {System.err.println("警告: 无法设置私钥文件权限");}
// 保存公钥String publicKeyContent = PublicKeyEntry.toString(keyPair.getPublic());Files.write(publicPath, publicKeyContent.getBytes(), StandardOpenOption.CREATE,StandardOpenOption.TRUNCATE_EXISTING);System.out.println("私钥已保存: " + privateKeyPath);System.out.println("公钥已保存: " + publicKeyPath);}/*** 使用密码认证上传公钥到远程服务器*/public static boolean uploadPublicKey(String host, String username, String password, String publicKeyPath,int port) {SshClient client = null;ClientSession session = null;try {client = SshClient.setUpDefaultClient();client.start();// 连接到服务器session = client.connect(username, host, port).verify(5000).getSession();// 使用密码认证session.addPasswordIdentity(password);if (!session.auth().verify(5000).isSuccess()) {System.err.println("密码认证失败");return false;}System.out.println("认证成功,开始上传公钥...");// 读取公钥内容String publicKeyContent = new String(Files.readAllBytes(Paths.get(publicKeyPath))).trim();// 创建 .ssh 目录(如果不存在)String mkdirCommand = "mkdir -p ~/.ssh && chmod 700 ~/.ssh";if (!executeCommand(session, mkdirCommand)) {System.err.println("创建 .ssh 目录失败");return false;}// 追加公钥到 authorized_keysString appendCommand = String.format("echo '%s' >> ~/.ssh/authorized_keys && chmod 600 ~/.ssh/authorized_keys", publicKeyContent);if (executeCommand(session, appendCommand)) {System.out.println("公钥上传成功!");return true;} else {System.err.println("公钥上传失败");return false;}} catch (Exception e) {System.err.println("上传公钥过程中出错: ");e.printStackTrace();return false;} finally {if (session != null) {try {session.close();} catch (IOException e) {}}if (client != null) {client.stop();}}}
/*** 执行远程命令*/private static boolean executeCommand(ClientSession session, String command) {try (ChannelExec channel = session.createExecChannel(command)) {ByteArrayOutputStream stdoutBaos = new ByteArrayOutputStream();ByteArrayOutputStream stderrBaos = new ByteArrayOutputStream();channel.setOut(stdoutBaos);channel.setErr(stderrBaos);channel.open().verify(5000);channel.waitFor(EnumSet.of(ClientChannelEvent.CLOSED), 0);if (stderrBaos.toByteArray().length > 0) {System.err.println("命令执行错误: " + new String(stderrBaos.toByteArray()));return false;}System.out.println(new String(stdoutBaos.toByteArray()));return channel.getExitStatus() == 0;} catch (Exception e) {System.err.println("执行命令失败: ");e.printStackTrace();return false;}}/*** 测试密钥连接*/public static boolean testKeyConnection(String host, String username, String privateKeyPath, int port, String password) {SshClient client = null;ClientSession session = null;try {client = SshClient.setUpDefaultClient();client.start();// 加载私钥// KeyPairResourceLoader loader = SecurityUtils.getKeyPairResourceLoader();KeyPairResourceLoader loader = SecurityUtils.getKeyPairResourceParser();Collection<KeyPair> keys = loader.loadKeyPairs(null, Paths.get(privateKeyPath), new SimplePasswordProvider(password));if (keys.isEmpty()) {System.err.println("无法加载私钥");return false;}KeyPair keyPair = keys.iterator().next();
// 连接到服务器session = client.connect(username, host, port).verify(5000).getSession();session.addPublicKeyIdentity(keyPair);if (session.auth().verify(5000).isSuccess()) {System.out.println("密钥认证测试成功!");executeCommand(session, "ls -ltra /");
return true;} else {System.err.println("密钥认证测试失败");return false;}} catch (Exception e) {System.err.println("测试连接失败: " );
e.printStackTrace();return false;} finally {if (session != null) {try {session.close();} catch (IOException e) {}
}if (client != null) {client.stop();}}}/*** 测试密钥连接*/public static boolean testKeyConnection(String host, String username, String privateKeyPath, int port) {SshClient client = null;ClientSession session = null;try {client = SshClient.setUpDefaultClient();client.start();// 加载私钥// KeyPairResourceLoader loader = SecurityUtils.getKeyPairResourceLoader();KeyPairResourceLoader loader = SecurityUtils.getKeyPairResourceParser();Collection<KeyPair> keys = loader.loadKeyPairs(null, Paths.get(privateKeyPath), null);if (keys.isEmpty()) {System.err.println("无法加载私钥");return false;}KeyPair keyPair = keys.iterator().next();// 连接到服务器session = client.connect(username, host, port).verify(5000).getSession();session.addPublicKeyIdentity(keyPair);if (session.auth().verify(5000).isSuccess()) {System.out.println("密钥认证测试成功!");executeCommand(session, "ls -ltra /");return true;} else {System.err.println("密钥认证测试失败");return false;}} catch (Exception e) {System.err.println("测试连接失败: " + e.getMessage());return false;} finally {if (session != null) {try {session.close();} catch (IOException e) {}}if (client != null) {client.stop();}}}
}
AdvancedKeyManager
import java.io.ByteArrayOutputStream;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.KeyPair;import org.apache.sshd.common.config.keys.KeyUtils;
import org.apache.sshd.common.config.keys.PublicKeyEntry;
import org.apache.sshd.common.config.keys.writer.openssh.OpenSSHKeyEncryptionContext;
import org.apache.sshd.common.config.keys.writer.openssh.OpenSSHKeyPairResourceWriter;public class AdvancedKeyManager {/*** 生成带密码的密钥对*/public static void generateEncryptedKeyPair(String privateKeyPath, String publicKeyPath, String keyPassword, String comment) throws Exception {
// 生成密钥对//KeyPair keyPair = KeyUtils.generateKeyPair(KeyUtils.RSA_ALGORITHM, 2048);KeyPair keyPair = KeyUtils.generateKeyPair("ssh-rsa", 2048);// 使用 OpenSSH 格式保存带密码的私钥OpenSSHKeyPairResourceWriter writer = new OpenSSHKeyPairResourceWriter();//String privateKeyContent = writer.writeKeyPair(keyPair, keyPassword);ByteArrayOutputStream out = new ByteArrayOutputStream();// 创建加密上下文OpenSSHKeyEncryptionContext encryptionContext = new OpenSSHKeyEncryptionContext();encryptionContext.setCipherName("AES"); // 使用 AES-256 加密
encryptionContext.setCipherType("256");encryptionContext.setPassword(keyPassword);writer.writePrivateKey(keyPair, comment, encryptionContext, out);Files.write(Paths.get(privateKeyPath), out.toByteArray());// 保存公钥String publicKeyContent = PublicKeyEntry.toString(keyPair.getPublic());Files.write(Paths.get(publicKeyPath), publicKeyContent.getBytes());System.out.println("带密码的密钥对生成完成");}
}
SimplePasswordProvider
import java.io.IOException;import org.apache.sshd.common.NamedResource;
import org.apache.sshd.common.config.keys.FilePasswordProvider;
import org.apache.sshd.common.session.SessionContext;public class SimplePasswordProvider implements FilePasswordProvider {private final String password;public SimplePasswordProvider(String password) {System.out.println("SimplePasswordProvider");this.password = password;}
@Overridepublic String getPassword(SessionContext session, NamedResource resourceKey, int retryIndex) throws IOException {System.out.println("getPassword");return password;}}