Connecting to SFTP server using 2-Factor authentication In Java


In this article we will connect to SFTP server using both password and SSH key as the authentication method. This is also referred to as 2-Factor Authentication with SFTP.

We will use Apache's VFS library to establish the connection.

Following class shows how to:

  • Connect to SFTP server
  • Navigate to a desired folder
  • List of files in a folder

First we need to add the Apache VFS dependency to our project. For maven it can be added to the pom.xml file.

<!-- https://mvnrepository.com/artifact/org.apache.commons/commons-vfs2 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-vfs2</artifactId>
<version>2.9.0</version>
</dependency>
Now let's look at the actual code, in SftpWithTwoFactorAuth class below.
/**
* This class uses Apache's VFS project to connect to remote SFTP server using
* 2-Factor authentication i.e username, password and also SSH key based authentication.
* <p>
* Maven Dependency: https://mvnrepository.com/artifact/org.apache.commons/commons-vfs2
* Apache Commons-VFS Project: https://commons.apache.org/proper/commons-vfs/
*/
public class SftpWithTwoFactorAuth {

public static final String SFTP = "sftp";

/**
* Holds instance of remote SFTP folder.
*/
private FileObject fileObject;

/**
* Direct instantiation not allowed. Use {@link #connect(String, Integer, String, String, File, String)}
* method to initiate connection.
*
* @param fileObject
*/
private SftpWithTwoFactorAuth(FileObject fileObject) {
this.fileObject = fileObject;
}

/**
* Initiate connection to remote SFTP server
*
* @param host
* @param port
* @param userName
* @param password - password to connect to remote SFTP
* @param sshKey - file containing the SSH private Key
* @param passphrase - Passphrase of SSH private key
* @return
*/
public static SftpWithTwoFactorAuth connect(String host,
Integer port,
String userName,
String password,
File sshKey,
String passphrase) throws URISyntaxException, FileSystemException {
URI sftpUri = createSftpUri(host, port, userName, password);

// Add SSK key as identity.
FileSystemOptions options = new FileSystemOptions();
SftpFileSystemConfigBuilder sftpConfigBuilder = SftpFileSystemConfigBuilder.getInstance();
IdentityProvider identityInfo = new IdentityInfo(sshKey, passphrase.getBytes());
sftpConfigBuilder.setIdentityProvider(options, identityInfo);

return new SftpWithTwoFactorAuth(VFS.getManager().resolveFile(sftpUri.toString(), options));
}

/**
* Create SFTP URI
*
* @param host
* @param port
* @param userName
* @param password
* @return
*/
private static URI createSftpUri(String host,
Integer port,
String userName,
String password) throws URISyntaxException {
String userInfo = userName + ":" + password;
return new URI(SFTP, userInfo, host, Objects.requireNonNullElse(port, -1), null, null, null);
}

/**
* Navigate to specific folder in SFTP
*
* @param folderPath
* @return
*/
public SftpWithTwoFactorAuth cd(String folderPath) throws FileSystemException {
if (this.fileObject == null) {
throw new RuntimeException("SFTP connection not initiated.");
}
FileObject newFolder = this.fileObject.resolveFile(folderPath);
if (!newFolder.exists() || !newFolder.isFolder()) {
throw new RuntimeException("Path does not exist or is not a folder.");
}
this.fileObject = newFolder;
return this;
}

/**
* List all file names. Only file names are listed, not the folders.
*
* @return
*/
public List<String> listFiles() throws FileSystemException {
return ofNullable(this.fileObject.findFiles(SELECT_FILES))
.map(Arrays::asList)
.orElseGet(Collections::emptyList)
.stream()
.map(FileObject::getName)
.map(FileName::getBaseName)
.collect(Collectors.toList());
}
}/**
* This class uses Apache's VFS project to connect to remote SFTP server using
* 2-Factor authentication i.e username, password and also SSH key based authentication.
* <p>
* Maven Dependency: https://mvnrepository.com/artifact/org.apache.commons/commons-vfs2
* Apache Commons-VFS Project: https://commons.apache.org/proper/commons-vfs/
*/
public class SftpWithTwoFactorAuth {

public static final String SFTP = "sftp";

/**
* Holds instance of remote SFTP folder.
*/
private FileObject fileObject;

/**
* Direct instantiation not allowed. Use {@link #connect(String, Integer, String, String, File, String)}
* method to initiate connection.
*
* @param fileObject
*/
private SftpWithTwoFactorAuth(FileObject fileObject) {
this.fileObject = fileObject;
}

/**
* Initiate connection to remote SFTP server
*
* @param host
* @param port
* @param userName
* @param password - password to connect to remote SFTP
* @param sshKey - file containing the SSH private Key
* @param passphrase - Passphrase of SSH private key
* @return
*/
public static SftpWithTwoFactorAuth connect(String host,
Integer port,
String userName,
String password,
File sshKey,
String passphrase) throws URISyntaxException, FileSystemException {
URI sftpUri = createSftpUri(host, port, userName, password);

// Add SSK key as identity.
FileSystemOptions options = new FileSystemOptions();
SftpFileSystemConfigBuilder sftpConfigBuilder = SftpFileSystemConfigBuilder.getInstance();
IdentityProvider identityInfo = new IdentityInfo(sshKey, passphrase.getBytes());
sftpConfigBuilder.setIdentityProvider(options, identityInfo);

return new SftpWithTwoFactorAuth(VFS.getManager().resolveFile(sftpUri.toString(), options));
}

/**
* Create SFTP URI
*
* @param host
* @param port
* @param userName
* @param password
* @return
*/
private static URI createSftpUri(String host,
Integer port,
String userName,
String password) throws URISyntaxException {
String userInfo = userName + ":" + password;
return new URI(SFTP, userInfo, host, Objects.requireNonNullElse(port, -1), null, null, null);
}

/**
* Navigate to specific folder in SFTP
*
* @param folderPath
* @return
*/
public SftpWithTwoFactorAuth cd(String folderPath) throws FileSystemException {
if (this.fileObject == null) {
throw new RuntimeException("SFTP connection not initiated.");
}
FileObject newFolder = this.fileObject.resolveFile(folderPath);
if (!newFolder.exists() || !newFolder.isFolder()) {
throw new RuntimeException("Path does not exist or is not a folder.");
}
this.fileObject = newFolder;
return this;
}

/**
* List all file names. Only file names are listed, not the folders.
*
* @return
*/
public List<String> listFiles() throws FileSystemException {
return ofNullable(this.fileObject.findFiles(SELECT_FILES))
.map(Arrays::asList)
.orElseGet(Collections::emptyList)
.stream()
.map(FileObject::getName)
.map(FileName::getBaseName)
.collect(Collectors.toList());
}
}
Sample usage of the SftpWithSshKey class would be as below
/**
* Sample usage of {@link SftpWithTwoFactorAuth} class
*/
class SftpWithTwoFactorAuthTest {

/**
* Test that all files in documents folder are listed.
*
* @throws URISyntaxException
* @throws FileSystemException
*/
public void listAllFiles() throws URISyntaxException, FileSystemException {
File privateKey = new File("~/.ssh/ida_rsa");
List<String> files = SftpWithTwoFactorAuth.connect("localhost", 22, "admin", "password", privateKey, "passphrase")
.cd("./documents")
.listFiles();
Assertions.assertEquals(2, files.size());
}
}

Connecting to Remote SFTP server using SSH key In Java


In this article we will connect to SFTP server using username and SSH key as the authentication method.

We will use Apache's VFS library to establish the connection.

Following class show how to:

  • Connect to SFTP server
  • Navigate to a desired folder
  • List of files in a folder
First we need to add the Apache VFS dependency to our project. For maven it can be added to the pom.xml file.

<!-- https://mvnrepository.com/artifact/org.apache.commons/commons-vfs2 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-vfs2</artifactId>
<version>2.9.0</version>
</dependency>
Now let's look at the actual code, in SftpWithSshKey class below.

/**
* This class uses Apache's VFS project to connect to remote SFTP server using
* username and SSH key based authentication.
* <p>
* Maven Dependency: https://mvnrepository.com/artifact/org.apache.commons/commons-vfs2
* Apache Commons-VFS Project: https://commons.apache.org/proper/commons-vfs/
*/
public class SftpWithSshKey {

public static final String SFTP = "sftp";

/**
* Holds instance of remote SFTP folder.
*/
private FileObject fileObject;

/**
* Direct instantiation not allowed. Use {@link #connect(String, Integer, String, File, String)}
* method to initiate connection.
*
* @param fileObject
*/
private SftpWithSshKey(FileObject fileObject) {
this.fileObject = fileObject;
}

/**
* Initiate connection to remote SFTP server
*
* @param host
* @param port
* @param userName
* @param sshKey - file containing the SSH private Key
* @param passphrase - Passphrase of SSH private key
* @return
*/
public static SftpWithSshKey connect(String host,
Integer port,
String userName,
File sshKey,
String passphrase) throws URISyntaxException, FileSystemException {
URI sftpUri = createSftpUri(host, port, userName);

FileSystemOptions options = new FileSystemOptions();
SftpFileSystemConfigBuilder sftpConfigBuilder = SftpFileSystemConfigBuilder.getInstance();
IdentityProvider identityInfo = new IdentityInfo(sshKey, passphrase.getBytes());
sftpConfigBuilder.setIdentityProvider(options, identityInfo);

return new SftpWithSshKey(VFS.getManager().resolveFile(sftpUri.toString(), options));
}

/**
* Navigate to specific folder in SFTP
*
* @param folderPath
* @return
*/
public SftpWithSshKey cd(String folderPath) throws FileSystemException {
if (this.fileObject == null) {
throw new RuntimeException("SFTP connection not initiated.");
}
FileObject newFolder = this.fileObject.resolveFile(folderPath);
if (!newFolder.exists() || !newFolder.isFolder()) {
throw new RuntimeException("Path does not exist or is not a folder.");
}
this.fileObject = newFolder;
return this;
}

/**
* List all file names. Only file names are listed, not the folders.
*
* @return
*/
public List<String> listFiles() throws FileSystemException {
return ofNullable(this.fileObject.findFiles(SELECT_FILES))
.map(Arrays::asList)
.orElseGet(Collections::emptyList)
.stream()
.map(FileObject::getName)
.map(FileName::getBaseName)
.collect(Collectors.toList());
}

/**
* Create SFTP URI
*
* @param host
* @param port
* @param userName
* @return
*/
private static URI createSftpUri(String host,
Integer port,
String userName) throws URISyntaxException {
return new URI(SFTP, userName, host, Objects.requireNonNullElse(port, -1), null, null, null);
}
}

Sample usage of the SftpWithSshKey class would be as below

/**
* Sample usage of {@link SftpWithSshKey} class
*/
class SftpWithSshKeyTest {

/**
* Test that all files in movies folder are listed.
*
* @throws URISyntaxException
* @throws FileSystemException
*/
public void listAllFiles() throws URISyntaxException, FileSystemException {
File privateKey = new File("~/.ssh/ida_rsa");
List<String> files = SftpWithSshKey.connect("localhost", 22, "admin", privateKey, "passphrase")
.cd("./documents")
.listFiles();
Assertions.assertEquals(2, files.size());
}
}