これは、なにをしたくて書いたもの?
最近使うことが減ってきた気がするSSHクライアントなどですが、Javaで扱おうとするとJSchが有名かと思います。
このあたりの事情を最近見直すとちょっと変わっていた感じがするのと、今回はApache MINA SSHDでSSHやSFTPの
クライアントを扱ってみたいと思います。
JavaのSSHクライアントライブラリー
Javaで使えるSSHクライアントライブラリーは、以前にまとめたことがあります。といっても13年前ですが…。
今調べ直すと、こんな感じになっていました。
JSch
Javaでよく使われるSSHクライアントライブラリーといえばJSchですが、現在はメンテナンスが止まっています。
JSch download | SourceForge.net
GitHub - is/jsch: Mirror of JSch from JCraft.
最後のリリースは、2018年でしょうか。
なお、フォークが存在します。現在はJSchといえばこちらを使う方もいるようです。
GitHub - mwiede/jsch: fork of the popular jsch library
Apache MINA SSHD
Apache MINA SSHDは、SSHクライアント・サーバーの両方を扱えるライブラリーです。
以前(これも10年くらい前ですが…)にSSHサーバーとして試したことがあります。
現在でもメンテナンスがアクティブに続けられています。
SSHJ
JSchと同じく、かなり前からあるSSHライブラリーです。
GitHub - hierynomus/sshj: ssh, scp and sftp for java
現在でもメンテナンスが続けられています。また操作もわかりやすそうです。
Ganymed SSH-2 for Java
こちらもJSchと同じく、かなり前からあるSSHライブラリーです。
こちらもメンテナンスはそれほどアクティブではなさそうです。最終リリースは2020年です。
Apache MINA SSHDを使う
JSchの更新が止まっていることを知らなかったので、ちょっと驚きました。
では今はどれが使われているのかなと思って調べてみたところ、Apache MINA SSHDが多そうです。
たとえばSpring IntegrationのSFTP Adaptersは、JSchからApache MINA SSHDに移行したようです。
Starting with version 6.0, an outdated JCraft JSch client has been replaced with modern Apache MINA SSHD framework. This caused a lot of breaking changes in the framework components.
SFTP Adapters :: Spring Integration
もっとも、利用者にはその変更があまりわからないようになっているみたいですが。
However, in most cases, such a migration is hidden behind Spring Integration API. The most drastic changed has happened with a DefaultSftpSessionFactory which is based now on the org.apache.sshd.client.SshClient and exposes some if its configuration properties.
ちなみにちょっと驚いたこととしては、Apache MINA SSHD側にSpring Integration互換のモジュールがあったりします。
https://github.com/apache/mina-sshd/tree/sshd-2.15.0/sshd-spring-sftp
Spring Integration自身はこのモジュールは使っていません。
というわけで、今回はApache MINA SSHDを見ていこうと思います。
https://github.com/apache/mina-sshd/tree/sshd-2.15.0/docs
サポートしている機能はこちら。
https://github.com/apache/mina-sshd/blob/sshd-2.15.0/docs/standards.md
基本的にREADME.mdからドキュメントを読み進めていけばよいでしょう。
https://github.com/apache/mina-sshd/tree/sshd-2.15.0
とはいえ、ドキュメントがちょっと読み解きづらいので、今回はメモを兼ねてSSHとSFTPを使ってみたいと思います。
環境
今回の環境はこちら。
$ java --version openjdk 21.0.6 2025-01-21 OpenJDK Runtime Environment (build 21.0.6+7-Ubuntu-124.04.1) OpenJDK 64-Bit Server VM (build 21.0.6+7-Ubuntu-124.04.1, mixed mode, sharing) $ mvn --version Apache Maven 3.9.9 (8e8579a9e76f7d015ee5ec7bfcdc97d260186937) Maven home: $HOME/.sdkman/candidates/maven/current Java version: 21.0.6, vendor: Ubuntu, runtime: /usr/lib/jvm/java-21-openjdk-amd64 Default locale: ja_JP, platform encoding: UTF-8 OS name: "linux", version: "6.8.0-55-generic", arch: "amd64", family: "unix"
また接続先のSSHサーバーは、Ubuntu Linux 24.04 LTSとします。
$ lsb_release -a No LSB modules are available. Distributor ID: Ubuntu Description: Ubuntu 24.04.2 LTS Release: 24.04 Codename: noble $ uname -srvmpio Linux 6.8.0-55-generic #57-Ubuntu SMP PREEMPT_DYNAMIC Wed Feb 12 23:42:21 UTC 2025 x86_64 x86_64 x86_64 GNU/Linux $ sshd -V OpenSSH_9.6p1 Ubuntu-3ubuntu13.8, OpenSSL 3.0.13 30 Jan 2024
IPアドレスは192.168.0.6、ポートは2022とします。
準備
SSHサーバー側の準備をします。
ユーザーを作成。
$ sudo adduser ssh-testuser
SSHサーバーには、パスワードと公開鍵認証の両方でログインできるようにしておきます。ふつうはやったとしても
後者だけだと思いますが、確認という意味で…。
$ sudo su - ssh-testuser
このユーザーのホームディレクトリーにファイルやディレクトリーを作成しておきます。
$ echo 'Hello from Server' > hello-server.txt $ mkdir remote-dir $ echo 'Hello from Server in remote-dir' > remote-dir/hello-server-in-dir.txt $ mkdir remote-dest-dir
ログイン用の公開鍵も作成しましょう。これはクライアント側で作成します。アルゴリズムはed25519です。
$ mkdir ssh-keys $ ssh-keygen -f ssh-keys/testkey Generating public/private ed25519 key pair. Enter passphrase (empty for no passphrase): Enter same passphrase again: Your identification has been saved in ssh-keys/testkey Your public key has been saved in ssh-keys/testkey.pub The key fingerprint is: SHA256:2I5UU/izS7EWXpXW+fTWf8yV8gPIswp67eJE+1su08E test@client The key's randomart image is: +--[ED25519 256]--+ | .. o.| | .. +.o| | o.. .o .=| | + .=+.o .*| | o.S..Bo ++o| | ..+. *E o=| | oo+ooo. o| | ..o.=+. | | o.o++. | +----[SHA256]-----+
$ ll ssh-keys 合計 16 drwxrwxr-x 2 xxxxx xxxxx 4096 3月 8 16:40 ./ drwxrwxr-x 5 xxxxx xxxxx 4096 3月 8 16:39 ../ -rw------- 1 xxxxx xxxxx 464 3月 8 16:40 testkey -rw-r--r-- 1 xxxxx xxxxx 93 3月 8 16:40 testkey.pub
公開鍵をサーバー側に置きます。
$ mkdir $HOME/.ssh $ echo 'ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIIeWtk8ud7ehRVLWEzaZIjcFHiMavsTJzf7HB6sfkIii test@client' > $HOME/.ssh/authorized_keys $ chmod 700 $HOME/.ssh $ chmod 600 $HOME/.ssh/authorized_keys
あとはApache MINA SSHDを使う準備をしておきましょう。
Maven依存関係など。
<properties> <maven.compiler.release>21</maven.compiler.release> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> </properties> <dependencies> <dependency> <groupId>org.apache.sshd</groupId> <artifactId>sshd-sftp</artifactId> <version>2.15.0</version> </dependency> <dependency> <groupId>net.i2p.crypto</groupId> <artifactId>eddsa</artifactId> <version>0.3.0</version> </dependency> <dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter</artifactId> <version>5.12.0</version> <scope>test</scope> </dependency> <dependency> <groupId>org.assertj</groupId> <artifactId>assertj-core</artifactId> <version>3.27.3</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-simple</artifactId> <version>1.7.36</version> <scope>test</scope> </dependency> </dependencies>
SFTPまで使うのでsshd-sftpを依存関係に含めていますが、SSHクライアントのみ使う場合はsshd-coreがあればOKです。
<dependency> <groupId>org.apache.sshd</groupId> <artifactId>sshd-core</artifactId> <version>2.15.0</version> </dependency>
Apache MINA SSHDはデフォルトでOpenSSHフォーマットの鍵を読むことができるのですが、アルゴリズムにed25519を
使っている場合には以下の依存関係が必要です。
<dependency> <groupId>net.i2p.crypto</groupId> <artifactId>eddsa</artifactId> <version>0.3.0</version> </dependency>
また、なぜかBouncy Castleを追加しても読めるようになります。
<dependency> <groupId>org.bouncycastle</groupId> <artifactId>bcpg-jdk18on</artifactId> <version>1.80</version> </dependency> <dependency> <groupId>org.bouncycastle</groupId> <artifactId>bcpkix-jdk18on</artifactId> <version>1.80</version> </dependency>
今回はeddsaを使いますが。
確認はテストコードで行うことにします。
これで準備完了です。
Apache MINA SSHDで、SSH/SFTPクライアントを使う
では、Apache MINA SSHDでSSH/SFTPクライアントを使っていきます。
以下の組み合わせでそれぞれ書いていきます。
- パスワード認証でSSH接続して、SSHでコマンド実行
- 公開鍵認証でSSH接続して、SSHでコマンド実行
- パスワード認証でSSH接続して、SFTP操作を実行
- 公開鍵認証でSSH接続して、SFTP操作を実行
クライアントサイドの場合、最初に見ておくドキュメントはこちらでしょうか。
https://github.com/apache/mina-sshd/blob/sshd-2.15.0/docs/security-providers.md
用意したテストコードの雛形はこちら。
src/test/java/org/littlewings/mina/sshd/SshClientTest.java
package org.littlewings.mina.sshd; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; import java.security.GeneralSecurityException; import java.security.KeyPair; import java.util.Collection; import java.util.EnumSet; import org.apache.sshd.client.SshClient; import org.apache.sshd.client.auth.password.PasswordIdentityProvider; import org.apache.sshd.client.channel.ClientChannel; import org.apache.sshd.client.channel.ClientChannelEvent; import org.apache.sshd.client.session.ClientSession; import org.apache.sshd.common.config.keys.FilePasswordProvider; import org.apache.sshd.common.keyprovider.KeyIdentityProvider; import org.apache.sshd.common.util.security.SecurityUtils; import org.apache.sshd.sftp.client.SftpClient; import org.apache.sshd.sftp.client.SftpClientFactory; import org.junit.jupiter.api.Test; import static org.assertj.core.api.Assertions.assertThat; class SshClientTest { private static final String SSH_HOST = "192.168.0.6"; private static final int SSH_PORT = 2022; private static final String SSH_USERNAME = "ssh-testuser"; private static final String SSH_PASSWORD = "password"; private static final String SSH_PRIVATE_KEY = "ssh-keys/testkey"; private static final String SSH_KEY_PASSPHRASE = "keypassword"; // ここに、テストを書く! }
ここからは、それぞれのバリエーションを書いていきます。
パスワード認証でSSH接続して、SSHでコマンド実行
最初は1番単純な(?)、パスワード認証でSSH接続してからコマンド実行します。
参考にするドキュメントはこちらです。
https://github.com/apache/mina-sshd/blob/sshd-2.15.0/docs/client-setup.md
作成したテストコードはこちら。
@Test void sshClientAuthenticatePassword() throws IOException { try (SshClient sshClient = SshClient.setUpDefaultClient()) { sshClient.start(); try (ClientSession clientSession = sshClient.connect(SSH_USERNAME, SSH_HOST, SSH_PORT).verify().getClientSession()) { // パスワード clientSession.setPasswordIdentityProvider(PasswordIdentityProvider.wrapPasswords(SSH_PASSWORD)); // ログイン clientSession.auth().verify(); try (ClientChannel clientChannel = clientSession.createExecChannel("ls -l")) { // 標準出力/エラー出力の設定 ByteArrayOutputStream outBaos = new ByteArrayOutputStream(); ByteArrayOutputStream errBaos = new ByteArrayOutputStream(); clientChannel.setOut(outBaos); clientChannel.setErr(errBaos); // コマンド実行 clientChannel.open().verify(); // コマンド終了待ち clientChannel.waitFor(EnumSet.of(ClientChannelEvent.CLOSED), 0L); System.out.println(outBaos.toString(StandardCharsets.UTF_8)); assertThat(outBaos.toString(StandardCharsets.UTF_8)) .contains("hello-server.txt", "remote-dir", "remote-dest-dir"); assertThat(errBaos.toString(StandardCharsets.UTF_8)).isEmpty(); } try (ClientChannel clientChannel = clientSession.createExecChannel("ls -l remote-dir")) { ByteArrayOutputStream outBaos = new ByteArrayOutputStream(); ByteArrayOutputStream errBaos = new ByteArrayOutputStream(); clientChannel.setOut(outBaos); clientChannel.setErr(errBaos); clientChannel.open().verify(); clientChannel.waitFor(EnumSet.of(ClientChannelEvent.CLOSED), 0L); System.out.println(outBaos.toString(StandardCharsets.UTF_8)); assertThat(outBaos.toString(StandardCharsets.UTF_8)) .contains("hello-server-in-dir.txt"); assertThat(errBaos.toString(StandardCharsets.UTF_8)).isEmpty(); } } } }
順を追って説明していきます。
最初にSshClientのインスタンスを作成。
try (SshClient sshClient = SshClient.setUpDefaultClient()) {
sshClient.start();
接続先やユーザー名を指定して、ClientSessionを作成します。
try (ClientSession clientSession = sshClient.connect(SSH_USERNAME, SSH_HOST, SSH_PORT).verify().getClientSession()) { // パスワード clientSession.setPasswordIdentityProvider(PasswordIdentityProvider.wrapPasswords(SSH_PASSWORD)); // ログイン clientSession.auth().verify();
パスワードの設定タイミングはClientSessionの取得後で、最後にClientSession#authからverifyを行うことでログインできます。
あとはコマンド実行ですが、ClientChannel経由で行います。
try (ClientChannel clientChannel = clientSession.createExecChannel("ls -l")) {
標準出力、標準エラー出力を設定できるので、今回はByteArrayOutputStreamに溜め込んで結果を確認できるようにしました。
// 標準出力/エラー出力の設定 ByteArrayOutputStream outBaos = new ByteArrayOutputStream(); ByteArrayOutputStream errBaos = new ByteArrayOutputStream(); clientChannel.setOut(outBaos); clientChannel.setErr(errBaos); // コマンド実行 clientChannel.open().verify(); // コマンド終了待ち clientChannel.waitFor(EnumSet.of(ClientChannelEvent.CLOSED), 0L); System.out.println(outBaos.toString(StandardCharsets.UTF_8)); assertThat(outBaos.toString(StandardCharsets.UTF_8)) .contains("hello-server.txt", "remote-dir", "remote-dest-dir"); assertThat(errBaos.toString(StandardCharsets.UTF_8)).isEmpty();
コマンド実行の後に
// コマンド実行
clientChannel.open().verify();
実行終了を待つところがポイントで、これを入れなかったら結果の取得が不安定になってとてもハマりました…。
// コマンド終了待ち clientChannel.waitFor(EnumSet.of(ClientChannelEvent.CLOSED), 0L);
こんなところですね。
ちなみに、標準出力に書き出された結果をSystem.out.printlnしている箇所がありますが、その結果はこちらです。
合計 12 -rw-rw-r-- 1 ssh-testuser ssh-testuser 18 3月 8 16:34 hello-server.txt drwxrwxr-x 2 ssh-testuser ssh-testuser 4096 3月 8 18:19 remote-dest-dir drwxrwxr-x 2 ssh-testuser ssh-testuser 4096 3月 8 16:34 remote-dir 合計 4 -rw-rw-r-- 1 ssh-testuser ssh-testuser 32 3月 8 18:18 hello-server-in-dir.txt
公開鍵認証でSSH接続して、SSHでコマンド実行
次は公開鍵認証を使って接続します。
結果はこちら。
@Test void sshClientAuthenticateKey() throws IOException, GeneralSecurityException { try (SshClient sshClient = SshClient.setUpDefaultClient()) { // SSH鍵の設定 Collection<KeyPair> keys = SecurityUtils .getKeyPairResourceParser() .loadKeyPairs(null, null, FilePasswordProvider.of(SSH_KEY_PASSPHRASE), Files.newBufferedReader(Path.of(SSH_PRIVATE_KEY), StandardCharsets.UTF_8)); sshClient.setKeyIdentityProvider(KeyIdentityProvider.wrapKeyPairs(keys)); sshClient.start(); try (ClientSession clientSession = sshClient.connect(SSH_USERNAME, SSH_HOST, SSH_PORT).verify().getClientSession()) { // ログイン clientSession.auth().verify(); try (ClientChannel clientChannel = clientSession.createExecChannel("ls -l")) { // 標準出力/エラー出力の設定 ByteArrayOutputStream outBaos = new ByteArrayOutputStream(); ByteArrayOutputStream errBaos = new ByteArrayOutputStream(); clientChannel.setOut(outBaos); clientChannel.setErr(errBaos); // コマンド実行 clientChannel.open().verify(); // コマンド終了待ち clientChannel.waitFor(EnumSet.of(ClientChannelEvent.CLOSED), 0L); System.out.println(outBaos.toString(StandardCharsets.UTF_8)); assertThat(outBaos.toString(StandardCharsets.UTF_8)) .contains("hello-server.txt", "remote-dir", "remote-dest-dir"); assertThat(errBaos.toString(StandardCharsets.UTF_8)).isEmpty(); } try (ClientChannel clientChannel = clientSession.createExecChannel("ls -l remote-dir")) { ByteArrayOutputStream outBaos = new ByteArrayOutputStream(); ByteArrayOutputStream errBaos = new ByteArrayOutputStream(); clientChannel.setOut(outBaos); clientChannel.setErr(errBaos); clientChannel.open().verify(); clientChannel.waitFor(EnumSet.of(ClientChannelEvent.CLOSED), 0L); System.out.println(outBaos.toString(StandardCharsets.UTF_8)); assertThat(outBaos.toString(StandardCharsets.UTF_8)) .contains("hello-server-in-dir.txt"); assertThat(errBaos.toString(StandardCharsets.UTF_8)).isEmpty(); } } } }
パスワードの設定はなくなりました。
try (ClientSession clientSession = sshClient.connect(SSH_USERNAME, SSH_HOST, SSH_PORT).verify().getClientSession()) { // ログイン clientSession.auth().verify();
// SSH鍵の設定 Collection<KeyPair> keys = SecurityUtils .getKeyPairResourceParser() .loadKeyPairs(null, null, FilePasswordProvider.of(SSH_KEY_PASSPHRASE), Files.newBufferedReader(Path.of(SSH_PRIVATE_KEY), StandardCharsets.UTF_8)); sshClient.setKeyIdentityProvider(KeyIdentityProvider.wrapKeyPairs(keys)); sshClient.start();
今回はSshClient全体に設定していますが、ClientSession単位とすることもできるようです。
Set up an SSH client in 5 minutes / ClientIdentityLoader/KeyPairProvider
あとの部分は同じですね。
ちなみに以下の依存関係が入っていなかった場合は
<dependency> <groupId>net.i2p.crypto</groupId> <artifactId>eddsa</artifactId> <version>0.3.0</version> </dependency>
こんな例外がスローされることになります(アルゴリズムをed25519にしている場合)。
java.security.NoSuchAlgorithmException: Unsupported key type (ssh-ed25519) in null
at org.apache.sshd.common.config.keys.loader.openssh.OpenSSHKeyPairResourceParser.readPublicKey(OpenSSHKeyPairResourceParser.java:221)
at org.apache.sshd.common.config.keys.loader.openssh.OpenSSHKeyPairResourceParser.extractKeyPairs(OpenSSHKeyPairResourceParser.java:133)
at org.apache.sshd.common.config.keys.loader.AbstractKeyPairResourceParser.extractKeyPairs(AbstractKeyPairResourceParser.java:198)
at org.apache.sshd.common.config.keys.loader.AbstractKeyPairResourceParser.extractKeyPairs(AbstractKeyPairResourceParser.java:167)
at org.apache.sshd.common.config.keys.loader.AbstractKeyPairResourceParser.loadKeyPairs(AbstractKeyPairResourceParser.java:117)
at org.apache.sshd.common.config.keys.loader.KeyPairResourceParser$2.loadKeyPairs(KeyPairResourceParser.java:166)
at org.apache.sshd.common.config.keys.loader.KeyPairResourceLoader.loadKeyPairs(KeyPairResourceLoader.java:157)
パスワード認証でSSH接続して、SFTP操作を実行
次はSFTPです。参照するドキュメントはこちら。
https://github.com/apache/mina-sshd/blob/sshd-2.15.0/docs/sftp.md
まずはパスワード認証です。実は、途中までは先ほどまでと同じです。
@Test void sftpClientAuthenticatePassword() throws IOException { try (SshClient sshClient = SshClient.setUpDefaultClient()) { sshClient.start(); try (ClientSession clientSession = sshClient.connect(SSH_USERNAME, SSH_HOST, SSH_PORT).verify().getClientSession()) { // パスワード clientSession.setPasswordIdentityProvider(PasswordIdentityProvider.wrapPasswords(SSH_PASSWORD)); // ログイン clientSession.auth().verify(); try (SftpClient sftpClient = SftpClientFactory.instance().createSftpClient(clientSession)) { // get try (InputStream is = sftpClient.read("hello-server.txt")) { ByteArrayOutputStream baos = new ByteArrayOutputStream(); is.transferTo(baos); assertThat(baos.toString(StandardCharsets.UTF_8)).isEqualTo("Hello from Server\n"); } try (InputStream is = sftpClient.read("remote-dir/hello-server-in-dir.txt")) { ByteArrayOutputStream baos = new ByteArrayOutputStream(); is.transferTo(baos); assertThat(baos.toString(StandardCharsets.UTF_8)).isEqualTo("Hello from Server in remote-dir\n"); } // put sftpClient.put(Path.of("pom.xml"), "remote-dest-dir/pom.xml"); // get try (InputStream is = sftpClient.read("remote-dest-dir/pom.xml")) { ByteArrayOutputStream baos = new ByteArrayOutputStream(); is.transferTo(baos); assertThat(baos.toString(StandardCharsets.UTF_8)).contains( "<groupId>org.apache.sshd</groupId>", "<artifactId>sshd-sftp</artifactId>" ); } } } } }
SftpClientを作成するのに、先ほどまで使っていたClientSessionが必要になります。
try (SftpClient sftpClient = SftpClientFactory.instance().createSftpClient(clientSession)) {
ファイル受信、送信など。
// get try (InputStream is = sftpClient.read("hello-server.txt")) { ByteArrayOutputStream baos = new ByteArrayOutputStream(); is.transferTo(baos); assertThat(baos.toString(StandardCharsets.UTF_8)).isEqualTo("Hello from Server\n"); } try (InputStream is = sftpClient.read("remote-dir/hello-server-in-dir.txt")) { ByteArrayOutputStream baos = new ByteArrayOutputStream(); is.transferTo(baos); assertThat(baos.toString(StandardCharsets.UTF_8)).isEqualTo("Hello from Server in remote-dir\n"); } // put sftpClient.put(Path.of("pom.xml"), "remote-dest-dir/pom.xml"); // get try (InputStream is = sftpClient.read("remote-dest-dir/pom.xml")) { ByteArrayOutputStream baos = new ByteArrayOutputStream(); is.transferTo(baos); assertThat(baos.toString(StandardCharsets.UTF_8)).contains( "<groupId>org.apache.sshd</groupId>", "<artifactId>sshd-sftp</artifactId>" ); }
送信するファイルは、pom.xmlにしました。
SftpClientを取得した後は、そこまで操作に迷うことはないと思います。
公開鍵認証でSSH接続して、SFTP操作を実行
最後は公開鍵認証を使って接続した後に、SFTP操作を行うパターン。
@Test void sftpClientAuthenticateKey() throws IOException, GeneralSecurityException { try (SshClient sshClient = SshClient.setUpDefaultClient()) { // SSH鍵の設定 Collection<KeyPair> keys = SecurityUtils .getKeyPairResourceParser() .loadKeyPairs(null, null, FilePasswordProvider.of(SSH_KEY_PASSPHRASE), Files.newBufferedReader(Path.of(SSH_PRIVATE_KEY), StandardCharsets.UTF_8)); sshClient.setKeyIdentityProvider(KeyIdentityProvider.wrapKeyPairs(keys)); sshClient.start(); try (ClientSession clientSession = sshClient.connect(SSH_USERNAME, SSH_HOST, SSH_PORT).verify().getClientSession()) { // ログイン clientSession.auth().verify(); try (SftpClient sftpClient = SftpClientFactory.instance().createSftpClient(clientSession)) { // get try (InputStream is = sftpClient.read("hello-server.txt")) { ByteArrayOutputStream baos = new ByteArrayOutputStream(); is.transferTo(baos); assertThat(baos.toString(StandardCharsets.UTF_8)).isEqualTo("Hello from Server\n"); } try (InputStream is = sftpClient.read("remote-dir/hello-server-in-dir.txt")) { ByteArrayOutputStream baos = new ByteArrayOutputStream(); is.transferTo(baos); assertThat(baos.toString(StandardCharsets.UTF_8)).isEqualTo("Hello from Server in remote-dir\n"); } // put sftpClient.put(Path.of("pom.xml"), "remote-dest-dir/pom.xml"); try (InputStream is = sftpClient.read("remote-dest-dir/pom.xml")) { ByteArrayOutputStream baos = new ByteArrayOutputStream(); is.transferTo(baos); assertThat(baos.toString(StandardCharsets.UTF_8)).contains( "<groupId>org.apache.sshd</groupId>", "<artifactId>sshd-sftp</artifactId>" ); } } } } }
ここまでくると、新しい要素はありません。
こんなところでしょうか。
おわりに
Apache MINA SSHDでSSH/SFTPクライアントを使ってみました。
ドキュメントのコード例に省略箇所が多かったのであまりイメージが掴めず試してみたのですが、まあそこそこハマりました。
まあ、なんとかなりました。
それにしても、Apache MINA自体がもともとネットワーク系のライブラリーだからか、利用側からするとやや低レイヤーな
感じもします。もう少し簡単に書きたい場合はSSHJの方が便利かもですね。