Java SFTP 服务器库? [关闭]

Posted

技术标签:

【中文标题】Java SFTP 服务器库? [关闭]【英文标题】:Java SFTP server library? [closed] 【发布时间】:2011-03-05 19:44:27 【问题描述】:

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

我正在尝试通过 SFTP 接收文件,但我似乎找不到任何 SFTP 服务器的实现。我找到了 FTP/SFTP/FTPS client 库和 FTP/FTPS 服务器库,但没有找到 SFTP 服务器。

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

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

【问题讨论】:

【参考方案1】:

如何使用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();
    

仅此而已。

【讨论】:

这会创建一个大多数现代客户端拒绝连接的 SSH 服务器 :-( 请参阅 ***.com/questions/33690689/…【参考方案2】:

请注意,SFTP 不是基于 SSL 的 FTP,也不是基于 SSH 的 FTP。 SFTP 服务器支持需要在 Java 中实现 SSHD。最好的选择是 Apache SSHD,

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

我从未使用过 SFTP,但我听说它很基本但很实用。

【讨论】:

好点。术语完全令人困惑。很多人认为 SFTP 是“安全 FTP”,或者是通过 SSL 或 SSH 或其他方式运行的 FTP 版本(但事实并非如此)。我希望他们把它叫做完全不同的东西。 链接失效了 而基于 SSL 的 FTP 被称为 FTPS,见de.wikipedia.org/wiki/FTP_%C3%BCber_SSL Mina SSHD 似乎已移至github.com/apache/mina-sshd【参考方案3】:

我尝试使用上述方法在 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();
  

【讨论】:

【参考方案4】:

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

但是轮询目录并不是一个坏主意——它可能比使用 j2ssh 设置您自己的 SFTP 服务器可靠得多。我已经记不清我遇到的执行这种轮询的应用程序的数量了,而且它通常工作得很好。

【讨论】:

我不知道它过去是否这样做,但它现在似乎不提供(n 开源)服务器。也许它已被转移到他们的商业产品中。【参考方案5】:

仅出于完整性考虑 - 我们维护的 SecureBlackbox 库提供了用于在 Java(包括 android)中创建您自己的 SSH/SFTP 服务器的类。

【讨论】:

链接坏了,现在是nsoftware.com/sftp/sftpserver。还应该提到的是,它具有商业许可证,没有公开定价信息。 @MichaelWyraz 不,那是不同的产品。 SecureBlackbox 就在那里,充满活力和活力。我现在会更新链接。【参考方案6】:

http://sourceforge.net/projects/javasecureftpd/

【讨论】:

您提供的链接不是java库,它是一个独立的产品。此外,它似乎没有使用 SFTP,而是使用 FTPS。您是否阅读过有关此产品的任何信息,或者您是否只是在 google 搜索“java secure ftp”时选择了第二个链接? 抱歉,我浏览了一个链接,上面说 javasecureftpd 正在实施 SFTP,我认为确实如此。无论如何,你不必刻薄,我只是想帮助你。【参考方案7】:

我正在使用 jftp http://j-ftp.sourceforge.net/ 从 j-ftp-*.tgz/j-ftp/dist 中提取 jftp.jar 唯一的问题 - 他们将 apache 类放在 jar 中(所以我必须手动删除 common-httpclient、log4j 包以避免冲突的依赖项)

【讨论】:

可能是因为问题是关于 SFTP 服务器,而不是客户端。

以上是关于Java SFTP 服务器库? [关闭]的主要内容,如果未能解决你的问题,请参考以下文章

Java SFTP 传输库 [关闭]

通过 SSIS 处理 SFTP 或 FTPS 文件的最佳方法 [关闭]

文件系统之-JAVA Sftp远程操作:

我的代码库-Java8实现FTP与SFTP文件上传下载

.NET 的 SFTP 库 [关闭]

共享密钥时使用 Java 客户端的 SFTP [重复]