Using Commons VFS to do SFTP

A few days back I was experimenting with Commons VFS to do SFTP for a project. Commons VFS uses JSch library to implement SFTP. Our project needed a solution where we are required to use a key based SSH logins. Also, our private part of the key has an “passphrase”. This tutorial will show how to use a custom folder to store id_rsa private key file protected with passphrase in Commons VFS.

Environment

  • Client OS: Windows 7 32 bit
  • Server OS: Ubuntu Server 11.04 on VirtualBox
  • IDE: Eclipse Indigo
  • Libraries:
    • Commons-VFS , version 2.0 (source code required)
    • with all dependencies listed here.

Problem Definition

I ran into two problems during my experimentation.

  1. First of all, how do I tell Commons-VFS to use a specific directory to find private ssh key in Windows OS environment.
  2. Second, how to use passphrase with VFS.

After searching in Google and browsing through VFS code base I’ve found out that you can set Java system environment variable named vfs.sftp.sshdir to point to your custom ssh key location. That solved my first problem. VFS kind of assumes that your environment is Linux or Cygwin and thus your .ssh key file will be found at conventionally named .ssh directory under the specific user under /home directory. In order to make VFS work in Windows environment you will need to set vfs.sftp.sshdir system variable to to a directory location where you have placed your private ssh key file with a name as “id_rsa”.

To solve second problem I had to modify the VFS source code in class named SftpClientFactory at package named org.apache.commons.vfs2.provider.sftp. Therefore you will need to download Commons-VFS source code to edit the named class.

Steps

Setting up SSH Keys

This tutorial assumes that reader knows how to setup a Windows client and a Linux server with proper SSH public and private keys. Basically you create (generate) SSH2 RSA based private and public key pairs. Save the public key on the Linux server attached to the user that you are going to use as login user. Put the private key file in a known location that will be accessed by VFS Java client. A good tutorial on how to setup SSH keys can be found here.

Modified VFS Code

Here is the modified Commons-VFS code which enabled me to use passphrase when using private key file. I’m only showing part of the code where I made changes at class named SftpClientFactory under package named org.apache.commons.vfs2.provider.sftp  at method createConnection. I’m only checking if a system property exist for with the name vfs.sftp.ssh.privatekeyfile.passphrase, and if a user do not supply passphrase all will be good too.


        if (identities != null)
        {
            for (int iterIdentities = 0; iterIdentities < identities.length; iterIdentities++)
            {
                final File privateKeyFile = identities[iterIdentities];
                try
                {
                	//iyusuf
                	String passPhrase;
                	passPhrase = System.getProperty("vfs.sftp.ssh.privatekeyfile.passphrase");
                	if (passPhrase != null){
                        jsch.addIdentity(privateKeyFile.getAbsolutePath(),passPhrase);
                	}else {
                        jsch.addIdentity(privateKeyFile.getAbsolutePath());                		
                	}
                }
                catch (final JSchException e)
                {
                    throw new FileSystemException("vfs.provider.sftp/load-private-key.error", privateKeyFile, e);
                }
            }
        }
        else
        {
            if (sshDir == null)
            {
                sshDir = findSshDir();
            }

            // Load the private key (rsa-key only)
            final File privateKeyFile = new File(sshDir, "id_rsa");
            if (privateKeyFile.isFile() && privateKeyFile.canRead())
            {
                try
                {
                	//iyusuf
                	String passPhrase;
                	passPhrase = System.getProperty("vfs.sftp.ssh.privatekeyfile.passphrase");
                	if (passPhrase != null){
                        jsch.addIdentity(privateKeyFile.getAbsolutePath(),passPhrase);
                	}else {
                        jsch.addIdentity(privateKeyFile.getAbsolutePath());                		
                	}

                }
                catch (final JSchException e)
                {
                    throw new FileSystemException("vfs.provider.sftp/load-private-key.error", privateKeyFile, e);
                }
            }
        }

Java Client Code

Here is a small client code to test VFS with SFTP out. Here I’m logging in with private key file located on a folder on Windows. I’m basically listing folder contents from a remote Linux box.

public class VFSTest {
	public static void main(String[] args) throws Exception {
		FileSystemOptions fsOptions = new FileSystemOptions();
		SftpFileSystemConfigBuilder.getInstance().setStrictHostKeyChecking(fsOptions, "no");
		/* Using the following line will cause VFS to choose File System's Root as VFS's root.
		 * If I wanted to use User's home as VFS's root then I had to set 2nd method parameter 
		 * to "true"		 */
		SftpFileSystemConfigBuilder.getInstance().setUserDirIsRoot(fsOptions, false);
		FileSystemManager fsManager = VFS.getManager();

		String uri = "sftp://vfmaps@d1u9035g.austin.hp.com/";
		FileObject fo = fsManager.resolveFile(uri, fsOptions);
		
		FileObject appFolder = null;
		
		appFolder = fo.resolveFile("/u1",NameScope.DESCENDENT_OR_SELF);
		
		//List content of folder
		if (appFolder.isReadable()){
			System.out.println("n Get Children of appFolder");
			FileObject[] children = appFolder.getChildren();
			for ( int i = 0; i < children.length; i++ )
			{
			    System.out.println( children[ i ].getName().getBaseName() );
			}
		}
		
		fo.close();
		((DefaultFileSystemManager) fsManager).close();
		
	}
}

Conclusion:

Hope this tiny not so organized tutorial will help someone. Primary reason I put this tutorial on is that I could not find a lot of tutorial on the net when it comes to Commons-VFS and SFTP. Thanks everyone.

Resources:

Comments

  1. arittner says:

    You can use setIdentites in SftpFileSystemConfigBuilder to add private Key-Files.

  2. Joe says:

    This helped with another issue I was having. Thank you!

  3. rastaman says:

    Hi! thanks for your post, it’s interesting. Perhaps you should submit your patch to the project to not have to maintain your own patched version of commons-vfs. Best regards,

  4. Michael Nielsen says:

    you can use UserInfo to add private phrase
    SftpFileSystemConfigBuilder.getInstance().setUserInfo(fsOptions, new UserInfo() {

    @Override
    public void showMessage(String arg0) {
    }

    @Override
    public boolean promptYesNo(String arg0) {
    return false;
    }

    @Override
    public boolean promptPassword(String arg0) {
    return pwd != null ? true : false;
    }

    @Override
    public boolean promptPassphrase(String arg0) {
    return privatePhrase != null ? true : false;
    }

    @Override
    public String getPassword() {
    return pwd;
    }

    @Override
    public String getPassphrase() {
    return privatePhrase;
    }
    });

    Further you don’t close the filesystem properly.
    folder.close();
    FileSystem fs = folder.getFileSystem();
    FileSystemManager fsManager = VFS.getManager();
    fsManager.closeFileSystem(fs);

Leave a Reply