2010-06-19 131 views
32

是否有可用于实现SFTP服务器的Java库?Java SFTP服务器库?

我试图通过SFTP接收文件,但我似乎无法找到一个SFTP服务器的任何实现。我发现FTP/SFTP/FTPS 客户端库和FTP/FTPS服务器库,但没有用于SFTP服务器。

为了澄清,我想通过SFTP收到文件。不从我的应用程序“获取”或“放置”到另一个现有服务器。

现在我的应用程序可以让用户连接到本地的Linux SFTP服务器,删除文件,然后我的应用程序轮询的目录,但我觉得这是一个糟糕的实现;我讨厌“轮询”目录,但不幸的是他们必须使用SFTP。有什么建议么?

回答

40

如何设置使用Apache Mina SSHD SFTP服务器:

public void setupSftpServer(){ 
    SshServer sshd = SshServer.setUpDefaultServer(); 
    sshd.setPort(22); 
    sshd.setKeyPairProvider(new SimpleGeneratorHostKeyProvider("hostkey.ser")); 

    List<NamedFactory<UserAuth>> userAuthFactories = new ArrayList<NamedFactory<UserAuth>>(); 
    userAuthFactories.add(new UserAuthNone.Factory()); 
    sshd.setUserAuthFactories(userAuthFactories); 

    sshd.setCommandFactory(new ScpCommandFactory()); 

    List<NamedFactory<Command>> namedFactoryList = new ArrayList<NamedFactory<Command>>(); 
    namedFactoryList.add(new SftpSubsystem.Factory()); 
    sshd.setSubsystemFactories(namedFactoryList); 

    try { 
     sshd.start(); 
    } catch (Exception e) { 
     e.printStackTrace(); 
    } 
} 

而这一切。

+0

这创建了一个大多数现代客户端都会拒绝连接的SSH服务器:-(请参阅http://stackoverflow.com/questions/33690689/no-matching-host-key-type-found-apache-mina-sftp-server – Rich 2015-11-13 10:35:04

0
+0

您所提供的链接是不一个Java库,它是一个独立的产品。此外,它似乎并未使用SFTP,而是使用FTPS。您是否阅读过有关此产品的任何内容,或者您​​是否在搜索“java secure ftp”时选择了第二个链接? – 2010-06-19 17:54:08

+5

我的道歉,我通过一个链接,说javasecureftpd正在实施SFTP去了,我还以为它没有。无论如何,你不必是卑鄙的,我只是想帮助你。 – pakore 2010-06-20 01:24:20

-1

我建议你看一看Apache Camel。它同时支持FTP,FTPS和SFTP

+1

下面是您链接的Apache Camel网站的直接引用: “此组件提供通过FTP和SFTP协议访问远程文件系统的权限。” 要点是“访问远程文件系统”,这不允许您托管自己的SFTP服务器。 – 2010-06-20 14:24:57

4

请注意,SFTP是通过SSL进行不FTP,也不FTP通过SSH。 SFTP服务器支持需要在Java中实现SSHD。最好的办法是Apache的SSHD,

http://mina.apache.org/sshd-project/

我从来没有使用过SFTP,但我听说这是基本的,但功能。

+0

好点。这个术语很混乱。许多人认为SFTP是“Secure FTP”,或者是通过SSL或SSH运行的FTP版本,或者其他的东西(事实并非如此)。我希望他们可以称之为完全不同的东西。 – hotshot309 2013-11-19 16:12:26

+0

链接已经死了btw – Tommy 2015-04-23 07:21:27

1

看看SSHTools (j2ssh)。它包括一个客户端和服务器。

然而轮询的目录是不是坏主意 - 它可能比建立自己的SFTP服务器使用J2SSH更可靠。我已经失去了我遇到的那种进行这种轮询的应用程序数量,并且它通常工作得很好。

+0

我不知道它是否在过去,但现在似乎没有提供(n开源)服务器。也许它已经转移到他们的商业产品。 – 2016-04-20 14:36:01

0

我使用JFTP http://j-ftp.sourceforge.net/ 提取jftp.jar从JFTP - * TGZ/JFTP/DIST 唯一的问题 - 他们把罐子有Apache内部类(所以我必须去除共HttpClient的, log4j的包手动,以避免冲突的依赖)

+0

为什么要投票? – 2012-10-27 00:13:24

+1

可能因为问题是关于SFTP服务器,而不是客户端。 – FelixM 2014-05-20 14:50:56

1

只是为了完整性 - 我们SecureBlackbox(Java版本)库提供类Java创建(包括Android)你自己的SSH/SFTP服务器。

+3

它是商业。 – Alvins 2013-07-29 12:46:29

2

我试图在Windows上做MINA 0.10.1上述和固定的一些问题,加上我需要(仍然不建议在生产中使用)更好的认证和支持PK:

import java.io.File; 
import java.io.ByteArrayOutputStream; 
import java.io.DataOutputStream; 
import java.io.PrintWriter; 

import java.util.Arrays; 
import java.util.Map; 
import java.util.HashMap; 
import java.util.Scanner; 

import java.math.BigInteger; 

import java.security.PublicKey; 
import java.security.interfaces.RSAPublicKey; 
import java.security.interfaces.DSAPublicKey; 

import java.security.KeyFactory; 

import java.security.spec.KeySpec; 
import java.security.spec.DSAPublicKeySpec; 
import java.security.spec.RSAPublicKeySpec; 

import org.apache.sshd.common.NamedFactory; 

import org.apache.sshd.SshServer; 
import org.apache.sshd.server.Command; 
import org.apache.sshd.server.command.ScpCommandFactory; 
import org.apache.sshd.server.keyprovider.SimpleGeneratorHostKeyProvider; 
import org.apache.sshd.server.PasswordAuthenticator; 
import org.apache.sshd.server.PublickeyAuthenticator; 
import org.apache.sshd.server.session.ServerSession; 
import org.apache.sshd.server.sftp.SftpSubsystem; 
import org.apache.sshd.server.shell.ProcessShellFactory; 
import org.apache.sshd.server.UserAuth; 
import org.apache.sshd.server.auth.UserAuthPassword; 
import org.apache.sshd.server.auth.UserAuthPublicKey; 

import org.apache.sshd.common.KeyExchange; 
//import org.apache.sshd.server.kex.DHGEX; 
//import org.apache.sshd.server.kex.DHGEX256; 
import org.apache.sshd.server.kex.ECDHP256; 
import org.apache.sshd.server.kex.ECDHP384; 
import org.apache.sshd.server.kex.ECDHP521; 
import org.apache.sshd.server.kex.DHG1; 

import org.apache.mina.util.Base64; 
/* 
javac -classpath .;lib/sshd-core-0.10.1.jar;lib/mina-core-2.0.7.jar;lib/waffle-jna.jar;lib/guava-13.0.1.jar;lib/jna-platform-4.0.0.jar;lib/jna-4.0.0.jar SFTPServer.java 
java -classpath .;lib/sshd-core-0.10.1.jar;lib/slf4j-simple-1.7.6.jar;lib/slf4j-api-1.6.6.jar;lib/mina-core-2.0.7.jar;lib/waffle-jna.jar;lib/guava-13.0.1.jar;lib/jna-platform-4.0.0.jar;lib/jna-4.0.0.jar SFTPServer 
*/ 
public class SFTPServer { 
    public void setupSftpServer() throws Exception { 

    class AuthorizedKeyEntry { 
     private String keyType; 
     private String pubKey; 

     private byte[] bytes; 
     private int pos; 
     private PublicKey key = null; 

     private int decodeInt() { 
     return ((bytes[pos++] & 0xFF) << 24) | ((bytes[pos++] & 0xFF) << 16) 
       | ((bytes[pos++] & 0xFF) << 8) | (bytes[pos++] & 0xFF); 
     } 

     private BigInteger decodeBigInt() { 
     int len = decodeInt(); 
     byte[] bigIntBytes = new byte[len]; 
     System.arraycopy(bytes, pos, bigIntBytes, 0, len); 
     pos += len; 
     return new BigInteger(bigIntBytes); 
     } 

     private void decodeType() { 
     int len = decodeInt(); 
     keyType = new String(bytes, pos, len); 
     pos += len; 
     } 

     public PublicKey getPubKey() { 
     return key; 
     } 

     public void setPubKey(PublicKey key) throws Exception { 
     this.key = key; 
     ByteArrayOutputStream byteOs = new ByteArrayOutputStream(); 
     DataOutputStream dos = new DataOutputStream(byteOs); 
     if (key instanceof RSAPublicKey) { 
      keyType = "ssh-rsa"; 
      dos.writeInt(keyType.getBytes().length); 
      dos.write(keyType.getBytes()); 

      RSAPublicKey rsakey = (RSAPublicKey)key; 
      BigInteger e = rsakey.getPublicExponent(); 
      dos.writeInt(e.toByteArray().length); 
      dos.write(e.toByteArray()); 
      BigInteger m = rsakey.getModulus(); 
      dos.writeInt(m.toByteArray().length); 
      dos.write(m.toByteArray()); 
     } else if (key instanceof DSAPublicKey) { 
      keyType = "ssh-dss"; 
      dos.writeInt(keyType.getBytes().length); 
      dos.write(keyType.getBytes()); 

      DSAPublicKey dsskey = (DSAPublicKey)key; 
      BigInteger p = dsskey.getParams().getP(); 
      dos.writeInt(p.toByteArray().length); 
      dos.write(p.toByteArray()); 
      BigInteger q = dsskey.getParams().getQ(); 
      dos.writeInt(q.toByteArray().length); 
      dos.write(q.toByteArray()); 
      BigInteger g = dsskey.getParams().getG(); 
      dos.writeInt(g.toByteArray().length); 
      dos.write(g.toByteArray()); 
      BigInteger y = dsskey.getY(); 
      dos.writeInt(y.toByteArray().length); 
      dos.write(y.toByteArray()); 
     } else { 
      throw new IllegalArgumentException("unknown key encoding " + key.getAlgorithm()); 
     } 
     bytes = byteOs.toByteArray(); 
     this.pubKey = new String(Base64.encodeBase64(bytes)); 
     } 

     public void setPubKey(String pubKey) throws Exception { 
     this.pubKey = pubKey; 
     bytes = Base64.decodeBase64(pubKey.getBytes()); 
     if (bytes == null) 
      return; 
     decodeType(); 
     if (keyType.equals("ssh-rsa")) { 
      BigInteger e = decodeBigInt(); 
      BigInteger m = decodeBigInt(); 
      KeySpec spec = new RSAPublicKeySpec(m, e); 
      key = KeyFactory.getInstance("RSA").generatePublic(spec); 
     } else if (keyType.equals("ssh-dss")) { 
      BigInteger p = decodeBigInt(); 
      BigInteger q = decodeBigInt(); 
      BigInteger g = decodeBigInt(); 
      BigInteger y = decodeBigInt(); 
      KeySpec spec = new DSAPublicKeySpec(y, p, q, g); 
      key = KeyFactory.getInstance("DSA").generatePublic(spec); 
     } else { 
      throw new IllegalArgumentException("unknown type " + keyType); 
     } 
     } 
    } 

    final SshServer sshd = SshServer.setUpDefaultServer(); 
    final Map<ServerSession, PublicKey> sessionKeys = new HashMap(); 

    class AuthorizedKeys extends HashMap<String,AuthorizedKeyEntry> { 
     private File file; 


     public void load(File file) throws Exception { 
     this.file = file; 
     Scanner scanner = new Scanner(file).useDelimiter("\n"); 
     while (scanner.hasNext()) 
      decodePublicKey(scanner.next()); 
     scanner.close(); 
     } 

     public void save() throws Exception { 
     PrintWriter w = new PrintWriter(file); 
     for (String username : keySet()) { 
      AuthorizedKeyEntry entry = get(username); 
      w.print(entry.keyType + " " + entry.pubKey + " " + username + "\n"); 
     } 
     w.close(); 
     } 

     public void put(String username, PublicKey key) { 
     AuthorizedKeyEntry entry = new AuthorizedKeyEntry(); 
     try { 
      entry.setPubKey(key); 
     } catch (Exception e) { 
      e.printStackTrace(); 
     } 
     super.put(username,entry); 
     } 

     private void decodePublicKey(String keyLine) throws Exception { 
     AuthorizedKeyEntry entry = new AuthorizedKeyEntry(); 
     String[] toks = keyLine.split(" "); 
     String username = toks[toks.length-1]; 
     for (String part : toks) { 
      if (part.startsWith("AAAA")) { 
      entry.setPubKey(part); 
      //bytes = Base64.decodeBase64(part.getBytes()); 
      break; 
      } 
     } 
     super.put(username,entry); 
     } 
    }; 

    final AuthorizedKeys authenticUserKeys = new AuthorizedKeys(); // load authorized_keys 
    File file = new File("authorized_keys"); 
    file.createNewFile(); // create if not exists 
    authenticUserKeys.load(file); 


    sshd.setPort(22); 
    sshd.setKeyPairProvider(new SimpleGeneratorHostKeyProvider("key.ser")); 

    sshd.setShellFactory(new ProcessShellFactory(new String[] { "cmd.exe "})); 

    sshd.setPasswordAuthenticator(new PasswordAuthenticator() { 
     public boolean authenticate(String username, String password, ServerSession session) { 
     boolean authentic = false; 
     try { 
      new waffle.windows.auth.impl.WindowsAuthProviderImpl().logonUser(username,password); 
      authentic = true; 
      //authentic = username != null && username.equals(password+password); // obsecurity :) 
      if (authentic) { 
      PublicKey sessionKey = sessionKeys.get(session); 
      if (sessionKey != null) 
       authenticUserKeys.put(username, sessionKey); //save entry to authorized_keys 
      } 
     } catch (Exception e) { 
      System.err.println(e); 
     } 
     return authentic; 
     } 
    }); 

    sshd.setPublickeyAuthenticator(new PublickeyAuthenticator() { 
     public boolean authenticate(String username, PublicKey key, ServerSession session) { 
     sessionKeys.put(session,key); 
     return key.equals(authenticUserKeys.get(username).getPubKey()); 
     } 
    }); 

    sshd.setUserAuthFactories(Arrays.<NamedFactory<UserAuth>>asList(
     new UserAuthPublicKey.Factory() 
     ,new UserAuthPassword.Factory())); 

    sshd.setCommandFactory(new ScpCommandFactory()); 

    sshd.setSubsystemFactories(Arrays.<NamedFactory<Command>>asList(
     new SftpSubsystem.Factory())); 

    //workaround for apache sshd 10.0+ (putty) 
    sshd.setKeyExchangeFactories(Arrays.<NamedFactory<KeyExchange>>asList(
     //new DHGEX256.Factory() 
     //,new DHGEX.Factory() 
     new ECDHP256.Factory() 
     ,new ECDHP384.Factory() 
     ,new ECDHP521.Factory() 
     ,new DHG1.Factory())); 

    Runtime.getRuntime().addShutdownHook(new Thread() { 
     public void run() { 
     try { 
      authenticUserKeys.save(); 
      System.out.println("Stopping"); 
      sshd.stop(); 
     } catch (Exception e) { 
      e.printStackTrace(); 
     } 
     } 
    }); 

    System.out.println("Starting");  
    try { 
     sshd.start(); 
     Thread.sleep(Long.MAX_VALUE); 
    } catch (Exception e) { 
     e.printStackTrace(); 
    } 
    } 

    static public void main(String[] args) throws Exception { 
    new SFTPServer().setupSftpServer(); 
    } 
}