Skip to content

Instantly share code, notes, and snippets.

@ToastShaman
Created November 13, 2013 18:40
Show Gist options
  • Star 19 You must be signed in to star a gist
  • Fork 6 You must be signed in to fork a gist
  • Save ToastShaman/7454077 to your computer and use it in GitHub Desktop.
Save ToastShaman/7454077 to your computer and use it in GitHub Desktop.
Using an embedded Apache MINA SSHD server in a unit test to verify that your code is able to upload a file through SFTP. This unit tests uses JSch as the client to speak to an embedded Apache MINA sftp server and verifies that the upload of a text file was successful.
<dependency>
<groupId>org.apache.sshd</groupId>
<artifactId>sshd-sftp</artifactId>
<version>0.9.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.vngx</groupId>
<artifactId>vngx-jsch</artifactId>
<version>0.10</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.1</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.4</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest-all</artifactId>
<version>1.3</version>
<scope>test</scope>
</dependency>
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.equalTo;
import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.sshd.SshServer;
import org.apache.sshd.common.NamedFactory;
import org.apache.sshd.common.Session;
import org.apache.sshd.common.file.FileSystemView;
import org.apache.sshd.common.file.nativefs.NativeFileSystemFactory;
import org.apache.sshd.common.file.nativefs.NativeFileSystemView;
import org.apache.sshd.server.Command;
import org.apache.sshd.server.PasswordAuthenticator;
import org.apache.sshd.server.command.ScpCommandFactory;
import org.apache.sshd.server.keyprovider.SimpleGeneratorHostKeyProvider;
import org.apache.sshd.server.session.ServerSession;
import org.apache.sshd.sftp.subsystem.SftpSubsystem;
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import org.vngx.jsch.ChannelSftp;
import org.vngx.jsch.ChannelType;
import org.vngx.jsch.JSch;
import org.vngx.jsch.config.SessionConfig;
public class SftpServiceTest {
@Rule
public TemporaryFolder testFolder = new TemporaryFolder();
private static final String USERNAME = "username";
private static final String PASSWORD = "password";
private SshServer sshd;
@Before
public void prepare() throws IOException {
setupSSHServer();
}
private void setupSSHServer() throws IOException {
sshd = SshServer.setUpDefaultServer();
sshd.setFileSystemFactory(new NativeFileSystemFactory() {
@Override
public FileSystemView createFileSystemView(final Session session) {
return new NativeFileSystemView(session.getUsername(), false) {
@Override
public String getVirtualUserDir() {
return testFolder.getRoot().getAbsolutePath();
}
};
};
});
sshd.setPort(8001);
sshd.setSubsystemFactories(Arrays.<NamedFactory<Command>>asList(new SftpSubsystem.Factory()));
sshd.setCommandFactory(new ScpCommandFactory());
sshd.setKeyPairProvider(new SimpleGeneratorHostKeyProvider(testFolder.newFile("hostkey.ser").getAbsolutePath()));
sshd.setPasswordAuthenticator(new PasswordAuthenticator() {
@Override
public boolean authenticate(final String username, final String password, final ServerSession session) {
return StringUtils.equals(username, USERNAME) && StringUtils.equals(password, PASSWORD);
}
});
sshd.start();
}
@Test
public void testUploadingFiles() throws Exception {
sendFile("test.txt", "abc");
assertThat(new File(testFolder.getRoot(), "test.txt").exists(), equalTo(true));
}
@After
public void cleanup() throws InterruptedException {
try {
sshd.stop(true);
} catch (Exception e) {
// do nothing
}
}
private void sendFile(final String filename, final String contents) throws Exception {
SessionConfig config = new SessionConfig();
config.setProperty(SessionConfig.STRICT_HOST_KEY_CHECKING, "no");
org.vngx.jsch.Session session = JSch.getInstance().createSession(USERNAME, "localhost", 8001, config);
session.connect(PASSWORD.getBytes(StandardCharsets.UTF_8));
ChannelSftp sftpChannel = session.openChannel(ChannelType.SFTP);
sftpChannel.connect();
OutputStream out = sftpChannel.put(filename);
out.write(contents.getBytes(StandardCharsets.UTF_8));
IOUtils.closeQuietly(out);
sftpChannel.disconnect();
session.disconnect();
}
}
@sachinlala
Copy link

Hi Kevin (@ToastShaman) - this gist is very helpful. I have one doubt though - regarding "hostkey.ser" - is this your private-key file or do you have a file that can be used by anyone ?
Here's the issue I'm facing:
If I point to my RSA private-key file i.e. ~/.ssh/id_rsa, I get the error (I also tried to generate a PEM version of my PK file and referenced that, but same error):

com.jcraft.jsch.JSchException: invalid privatekey: [B@5824a83d
	at com.jcraft.jsch.KeyPair.load(KeyPair.java:747)
	at com.jcraft.jsch.KeyPair.load(KeyPair.java:561)
	at com.jcraft.jsch.IdentityFile.newInstance(IdentityFile.java:40)
	at com.jcraft.jsch.JSch.addIdentity(JSch.java:407)
	at com.jcraft.jsch.JSch.addIdentity(JSch.java:367)
	at com.walmart.ukgr.search.common.util.sftp.SFTPUtil.getSFTPSession(SFTPUtil.java:30)
	at com.walmart.ukgr.search.common.util.sftp.SFTPUtilTest.testSFTP(SFTPUtilTest.java:46)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at org.testng.internal.MethodInvocationHelper.invokeMethod(MethodInvocationHelper.java:85)
	at org.testng.internal.Invoker.invokeMethod(Invoker.java:639)
	at org.testng.internal.Invoker.invokeTestMethod(Invoker.java:816)
	at org.testng.internal.Invoker.invokeTestMethods(Invoker.java:1124)
	at org.testng.internal.TestMethodWorker.invokeTestMethods(TestMethodWorker.java:125)
	at org.testng.internal.TestMethodWorker.run(TestMethodWorker.java:108)
	at org.testng.TestRunner.privateRun(TestRunner.java:774)
	at org.testng.TestRunner.run(TestRunner.java:624)
	at org.testng.SuiteRunner.runTest(SuiteRunner.java:359)
	at org.testng.SuiteRunner.runSequentially(SuiteRunner.java:354)
	at org.testng.SuiteRunner.privateRun(SuiteRunner.java:312)
	at org.testng.SuiteRunner.run(SuiteRunner.java:261)
	at org.testng.SuiteRunnerWorker.runSuite(SuiteRunnerWorker.java:52)
	at org.testng.SuiteRunnerWorker.run(SuiteRunnerWorker.java:86)
	at org.testng.TestNG.runSuitesSequentially(TestNG.java:1191)
	at org.testng.TestNG.runSuitesLocally(TestNG.java:1116)
	at org.testng.TestNG.run(TestNG.java:1024)
	at org.testng.IDEARemoteTestNG.run(IDEARemoteTestNG.java:72)
	at org.testng.RemoteTestNGStarter.main(RemoteTestNGStarter.java:123)

Any help on this will be much appreciated.

Thanks.

@sachinlala
Copy link

I was able to resolve this by generating the PK file using bouncycastle lib - sharing the code here:

  private void genKey() throws Exception {
    KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
    keyPairGenerator.initialize(2048, new SecureRandom());
    PrivateKey privateKey = keyPairGenerator.genKeyPair().getPrivate();
    StringWriter writer = new StringWriter();
    try (PEMWriter pemWriter = new PEMWriter(writer)) {
      pemWriter.writeObject(privateKey);
    }
    FileUtils.writeByteArrayToFile(new File("<location for test PK file>"), writer.toString().getBytes());
  }

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment