Files.comExaVault

Java SFTP: Working JSch and sshj Examples, Plus the Way That Skips Both

SFTP & SSH

Most Java SFTP code was written against JSch and is still running in batch jobs nobody has opened in years. Here is the code for the two libraries a Java team chooses between today, JSch and sshj, which JSch to depend on, and a third way where Files.com connects to the other server and your Java talks to Files.com over HTTPS, plus host key verification, private keys in a build pipeline, and cleanup when a transfer fails.

Java SFTP code tends to outlive the people who wrote it. A surprising amount of it was written against JSch, the library that has anchored Java file transfer since the early 2000s, and a lot of it is still running inside batch jobs nobody wants to touch. That does not mean the code is bad.

It does mean a Java team eventually faces a choice between three ways: keep JSch and move to its maintained fork, use sshj for a cleaner API, or move the SFTP connection out of the JVM entirely and let Files.com make it.

Here is complete upload and download code for each, along with the details that short tutorials skip: which JSch to depend on, host key verification, private keys in a deployment, and cleanup when a transfer fails.

JSch, from the maintained fork

JSch remains the foundation of a great deal of Java SFTP code, and if you already use it a rewrite is rarely necessary. The decision that matters is which JSch. The original com.jcraft:jsch on SourceForge is no longer actively maintained and lacks the key exchange and signature algorithms current OpenSSH servers require. The maintained fork is a drop-in replacement:

<dependency>
    <groupId>com.github.mwiede</groupId>
    <artifactId>jsch</artifactId>
    <version>2.28.0</version>
</dependency>

Change the coordinates and the existing com.jcraft.jsch imports keep working. Here is a complete upload with SSH key authentication, which is what an unattended job wants:

import com.jcraft.jsch.ChannelSftp;
import com.jcraft.jsch.JSch;
import com.jcraft.jsch.Session;

public class SftpUpload {
    public static void main(String[] args) throws Exception {
        JSch jsch = new JSch();
        jsch.setKnownHosts("/etc/sftp/known_hosts");
        jsch.addIdentity("/etc/sftp/keys/brightway_ed25519");

        Session session = null;
        ChannelSftp sftp = null;
        try {
            session = jsch.getSession("alice", "sftp.example.com", 22);
            session.setConfig("StrictHostKeyChecking", "yes");
            session.connect();

            sftp = (ChannelSftp) session.openChannel("sftp");
            sftp.connect();

            sftp.put("/data/exports/orders.csv", "/inbound/orders.csv");
        } finally {
            if (sftp != null) sftp.disconnect();
            if (session != null) session.disconnect();
        }
    }
}

Three lines carry the security. setKnownHosts tells JSch which server host keys to trust. addIdentity loads the client's private key; pass the passphrase as a second argument if the key is encrypted. StrictHostKeyChecking set to yes stops JSch from silently trusting a server it has never seen. For password authentication, skip addIdentity and call session.setPassword("...") before connect().

Once the channel is open, put(local, remote) uploads, get(remote, local) downloads, ls(path) lists, and cd, mkdir, and rm do what they say:

for (Object entry : sftp.ls("/outbound")) {
    ChannelSftp.LsEntry file = (ChannelSftp.LsEntry) entry;
    System.out.println(file.getFilename());
}

sftp.get("/outbound/report.csv", "/data/imports/report.csv");

The line every JSch example gets wrong

A great many JSch snippets include session.setConfig("StrictHostKeyChecking", "no"), because it makes the first connection succeed with no setup. It also makes every connection succeed, including one to a server impersonating yours. SSH host keys are how your application proves it reached the intended server, and disabling the check means the client accepts any machine answering at that address.

Keep strict checking on and get the key into known_hosts the way SSH does it. Obtain the fingerprint from the server's administrator over a separate trusted channel, connect once from the service account or deployment environment, verify it, and save it. If the server's key later changes, the transfer fails until someone confirms why. That failure is a security control, not an inconvenience to work around.

sshj, for new projects

For a new integration, sshj offers a more modern API with current algorithm support:

<dependency>
    <groupId>com.hierynomus</groupId>
    <artifactId>sshj</artifactId>
    <version>0.39.0</version>
</dependency>
import net.schmizz.sshj.SSHClient;
import net.schmizz.sshj.sftp.SFTPClient;

public class SshjTransfer {
    public static void main(String[] args) throws Exception {
        try (SSHClient ssh = new SSHClient()) {
            ssh.loadKnownHosts();
            ssh.connect("sftp.example.com");
            ssh.authPublickey("alice", "/etc/sftp/keys/brightway_ed25519");

            try (SFTPClient sftp = ssh.newSFTPClient()) {
                sftp.put("/data/exports/orders.csv", "/inbound/orders.csv");
                sftp.get("/outbound/report.csv", "/data/imports/report.csv");
            }
        }
    }
}

loadKnownHosts() reads the current user's ~/.ssh/known_hosts by default, authPublickey authenticates with the key at that path, and both clients close themselves through try-with-resources. That last part matters more than it looks: a network failure can happen during connection, authentication, upload, or download, and structured cleanup means a failed transfer does not leave channels or sockets open.

The practical rule is simple. An existing JSch application moves to the maintained fork rather than rewriting working code. A new Java integration starts with sshj unless there is a specific reason not to. Both handle production transfers; the difference is migration cost and API preference.

Let Files.com make the SFTP connection

With either library, your application owns the SFTP connection, and that is more than a few calls to put() and get(). It is a private key in every environment, a host key per partner, a known_hosts file per deployment, an outbound port-22 rule, retries you write yourself, logs and alerts, credential rotation, and a different configuration for every partner.

Manageable for one or two endpoints. Hard for a team responsible for dozens.

The third option is to let Files.com connect to the partner's SFTP server on the cloud side, so the Java application talks to Files.com over HTTPS and never opens an SFTP connection of its own.

How a Remote Server Mount works

A Remote Server Mount connects an empty folder on your Files.com site to another server. Mount a partner's SFTP server at /partners/brightway and that folder becomes a live window onto it: an upload to /partners/brightway/inbound/orders.csv writes the file into the partner's /inbound directory as it arrives, and listing, renaming, deleting, and creating subfolders pass through the same way. Files.com keeps no copy.

The SFTP-specific configuration lives on the mount rather than in code: hostname, port, authentication, credential storage, host key, and the IP allowlist the partner maintains.

Files.com authenticates with a password, a private key, or both, and it can generate the key pair itself (RSA, 4096-bit): you give the public half to the partner's administrator and the private half stays inside Files.com. An existing key can be supplied in OpenSSH, PuTTY, or SSH2 format, and every credential is stored encrypted.

The host key is pinned the way the JSch section said to pin it. Files.com detects and stores the partner's host key on the first connection, or an administrator enters the fingerprint the partner supplied, and if that key ever changes Files.com disables the connection until an administrator approves the new one. The partner allowlists the Files.com IP addresses once.

The Java application uses HTTPS

Once the mount exists, the application uses the Files.com Java SDK with an API key over HTTPS:

<dependency>
    <groupId>com.files</groupId>
    <artifactId>files-sdk</artifactId>
</dependency>
import com.files.FilesClient;
import com.files.models.File;
import com.files.models.FileUploadPart;
import java.util.HashMap;

public class FilesTransfer {
    public static void main(String[] args) throws Exception {
        FilesClient.apiKey = System.getenv("FILES_API_KEY");

        FileUploadPart upload = File.beginUpload(
            "/partners/brightway/inbound/orders.csv", new HashMap<>());
        upload.putLocalFile("/data/exports/orders.csv");

        File report = File.download(
            "/partners/brightway/outbound/report.csv", new HashMap<>());
        report.saveAsLocalFile("/data/imports/report.csv");
    }
}

From the application's side this is an HTTPS upload and download. beginUpload returns a FileUploadPart, and putLocalFile streams the local file through it in parallel chunks with retries built in. File.download returns the file and saveAsLocalFile writes it to disk.

Files.com handles the SFTP connection to the partner behind the mounted folder, and the JVM no longer needs an SFTP library, the partner's private key, a known_hosts entry, outbound access on port 22, or any partner-specific connection logic.

Every operation lands in the Files.com audit log with a user, a file, and a timestamp. If your site runs on a custom domain, set the API root once with FilesClient.setProperty("apiRoot", "https://files.yourcompany.com").

The transfer may not need Java at all

Sometimes the best transfer code is none. Once the partner's server is a folder on Files.com, a Sync pushes to or pulls from it on a schedule, and an Automation acts on what arrives.

The documented pattern is a partner that drops invoices on its own SFTP server: Files.com pulls them every ten minutes, records each transfer, and a Move Files automation routes every file to its processing folder, with an alert if a run fails. Java stays responsible for the business logic, and the platform handles scheduling and delivery.

Existing JSch code keeps working too

Moving partner connections onto Files.com does not require every application to switch to HTTPS at once. Files.com also accepts inbound SFTP on port 22 with SSH key authentication.

Point the JSch or sshj code above at yourcompany.files.com, put into /partners/brightway/inbound, and Files.com proxies the transfer out to the partner's server. Instead of storing credentials and host keys for ten different servers, the application connects to one endpoint with one credential, and each partner is a separate mount.

A partner whose SFTP server sits inside a private network is reachable through the Files.com Agent, which connects outbound with no inbound firewall rule.

Credentials and keys in a Java deployment

Whichever way you choose, the transfer code is usually the easy part. Production failures come from credentials, file permissions, per-user configuration, and missing alerts.

Never hardcode a password, passphrase, or API key; read it from an environment variable or, better, inject it from a secrets manager, the way the System.getenv line above does.

Keep the private key and known_hosts file readable only by the service account, and test the transfer as that account, because a job that works from your shell can fail in production over nothing more than a different home directory or key permission.

Give every transfer a log line with a timestamp, source, destination, and result, return a nonzero exit status on failure, and let the scheduler alert. A Spring @Scheduled method or Quartz job that catches the exception and continues is how a partner waits a week for a missing file while the application looks healthy.

Which one to use

The maintained JSch fork when you have existing JSch code and want current algorithm support without a rewrite.

sshj when you are building a new integration and want a cleaner API with straightforward resource management.

A Files.com Remote Server Mount when you do not want the application to own partner-specific SFTP connections, or when the number of endpoints has made keys, host entries, retries, schedules, and audit requirements hard to manage.

If Java is not the language for the job, the same three ways are covered for Python, PowerShell, C#, and Node.js, and the SFTP automation guide covers bash and cron.

When code stops being the right answer

A few SFTP jobs inside a Java service are reasonable. The trouble starts when each job has a different key, fingerprint, firewall rule, schedule, retry policy, and owner. At that point the team is no longer maintaining a small integration. It is operating a file-transfer platform, whether it meant to or not.

That is where teams move the transfers onto a platform built to run them. Files.com is the cloud-native File Orchestration Platform: one platform that replaces the stack of legacy tools IT teams run to move files, including the SFTP servers, the jobs that feed them, and the schedulers holding it all together. It speaks every protocol, connects 50+ cloud and on-prem systems, automates every transfer, and keeps a complete audit trail.

For the job in this post, that means the scheduled upload becomes a Files.com automation with retries and a delivery record, and Java stays for the logic that is genuinely yours, calling the SDK when it needs to take part. If the SFTP server has to stay in your own data center, the ExaVault appliance is a free SFTP server for up to 50 users that every example above can target.

Start a free Files.com trial and point the SDK at it. No credit card required, and an SFTP endpoint with SSH key support is live in minutes.

Frequently asked questions

Which JSch should I use?

The maintained fork, com.github.mwiede:jsch. It is a drop-in replacement for the original com.jcraft:jsch, which is no longer actively maintained and lacks the algorithms modern OpenSSH servers require. Change the Maven coordinates and the existing imports keep working.

How do I upload a file over SFTP in Java?

With JSch, call getSession, connect, openChannel("sftp"), then ChannelSftp.put(local, remote), and disconnect both objects in a finally block. With sshj, call SSHClient.connect, authPublickey, and newSFTPClient().put(local, remote) inside try-with-resources. With Files.com, call File.beginUpload(path, params).putLocalFile(local) over HTTPS, and a Remote Server Mount carries the file on to the partner's SFTP server.

How do I use an SSH key with JSch?

Call jsch.addIdentity(privateKeyPath) before getSession, with the passphrase as a second argument if the key is encrypted, and skip setPassword. In production the passphrase comes from a secrets manager, not from the code.

Is StrictHostKeyChecking=no safe?

No. It lets the client accept any server host key, including an attacker's, which defeats SSH's identity check. Keep it at yes, store the verified key in known_hosts for the account that runs the job, and investigate any unexpected key change.

Can Files.com connect to my partner's SFTP server for me?

Yes. A Remote Server Mount connects a folder on your Files.com site to any SFTP server in real time. Files.com holds the credentials, can generate the key pair, pins the partner's host key, and passes every operation through. Your Java uploads to Files.com over HTTPS with the SDK, or a scheduled Sync moves the files with no code at all.

FTP, SFTP, FTPS — in a Modern UI

Files.com is the cloud File Orchestration Platform. Bring your FTP clients; pick up a real web file manager, share links, automations, and SOC 2 / HIPAA-BAA compliance.