最近我们的一个客户意外地将我们从 ftp 收集的一些重要文件转移到 sftp 服务器。最初我的印象是编写或找到可以处理 sftp 的 java 实用程序很简单,但事实证明并非如此。使这个问题更加复杂的是,我们正试图从 Windows 平台连接到 sftp 服务器(因此客户端上 SSH_HOME 所在位置的定义变得非常混乱)。
我一直在使用 apache-commons-vfs 库,并设法获得了一个可靠地用于用户名/密码身份验证的解决方案,但到目前为止还没有任何可以可靠地处理私钥/公钥身份验证的解决方案。
以下示例适用于用户名/密码身份验证,但我想针对私钥/公钥身份验证对其进行调整。
public static void sftpGetFile(String server, String userName,String password,
String remoteDir, String localDir, String fileNameRegex)
{
File localDirFile = new File(localDir);
FileSystemManager fsManager = null;
if (!localDirFile.exists()) {
localDirFile.mkdirs();
}
try {
fsManager = VFS.getManager();
} catch (FileSystemException ex) {
LOGGER.error("Failed to get fsManager from VFS",ex);
throw new RuntimeException("Failed to get fsManager from VFS", ex);
}
UserAuthenticator auth = new StaticUserAuthenticator(null, userName,password);
FileSystemOptions opts = new FileSystemOptions();
try {
DefaultFileSystemConfigBuilder.getInstance().setUserAuthenticator(opts,
auth);
} catch (FileSystemException ex) {
LOGGER.error("setUserAuthenticator failed", ex);
throw new RuntimeException("setUserAuthenticator failed", ex);
}
Pattern filePattern = Pattern.compile(fileNameRegex);
String startPath = "sftp://" + server + remoteDir;
FileObject[] children;
// Set starting path on remote SFTP server.
FileObject sftpFile;
try {
sftpFile = fsManager.resolveFile(startPath, opts);
LOGGER.info("SFTP connection successfully established to " +
startPath);
} catch (FileSystemException ex) {
LOGGER.error("SFTP error parsing path " +
remoteDir,
ex);
throw new RuntimeException("SFTP error parsing path " +
remoteDir,
ex);
}
// Get a directory listing
try {
children = sftpFile.getChildren();
} catch (FileSystemException ex) {
throw new RuntimeException("Error collecting directory listing of " +
startPath, ex);
}
search:
for (FileObject f : children) {
try {
String relativePath =
File.separatorChar + f.getName().getBaseName();
if (f.getType() == FileType.FILE) {
System.out.println("Examining remote file " + f.getName());
if (!filePattern.matcher(f.getName().getPath()).matches()) {
LOGGER.info(" Filename does not match, skipping file ." +
relativePath);
continue search;
}
String localUrl = "file://" + localDir + relativePath;
String standardPath = localDir + relativePath;
System.out.println(" Standard local path is " + standardPath);
LocalFile localFile =
(LocalFile) fsManager.resolveFile(localUrl);
System.out.println(" Resolved local file name: " +
localFile.getName());
if (!localFile.getParent().exists()) {
localFile.getParent().createFolder();
}
System.out.println(" ### Retrieving file ###");
localFile.copyFrom(f,
new AllFileSelector());
} else {
System.out.println("Ignoring non-file " + f.getName());
}
} catch (FileSystemException ex) {
throw new RuntimeException("Error getting file type for " +
f.getName(), ex);
}
}
FileSystem fs = null;
if (children.length > 0) {
fs = children[0].getFileSystem(); // This works even if the src is closed.
fsManager.closeFileSystem(fs);
}
}
我已经将我的私钥存储在一个已知位置并且我的公钥已分发到服务器(我们已经测试过这些密钥在使用其他工具连接时可以成功运行)
我玩过添加以下行
SftpFileSystemConfigBuilder.getInstance().setIdentities(this.opts, new File[]{new File("c:/Users/bobtbuilder/.ssh/id_dsa.ppk")});
这成功地将私钥加载到整个框架中,但它永远不会使用该密钥进一步进行身份验证。
最热烈接受的任何帮助或指示
原文由 grumblebee 发布,翻译遵循 CC BY-SA 4.0 许可协议
经过大量挖掘后,我终于自己找到了答案。似乎我的大部分烦恼都与私钥和公钥的格式有关
privateKey 必须采用 openSSH 格式 publicKey 无论出于何种原因只能从 puttyGen 窗口粘贴(导出公钥似乎总是缺少标头,这意味着 freeSSHD windows 服务器无法使用它)
无论如何,下面是我最终想出的代码,包括 javadoc,所以希望能为其他人省去我经历的痛苦