2010-06-19 128 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(); 
    } 
}