web-dev-qa-db-ja.com

Java SFTPサーバーライブラリ?

[〜#〜] sftp [〜#〜] サーバーを実装するために使用できるJavaライブラリーはありますか?

SFTP経由でファイルを受信しようとしていますが、SFTPサーバーの実装を見つけることができないようです。 FTP/SFTP/FTPSclientライブラリ、およびFTP/FTPSサーバーライブラリが見つかりましたが、SFTP用のサーバーはありません。

明確にするために、SFTP経由でファイルをreceiveしようとしています。アプリケーションから既存の別のサーバーにファイルを「取得」または「配置」しないでください。

現在、私のアプリケーションでは、ユーザーがローカルlinux SFTPサーバーに接続してファイルをドロップし、次にディレクトリをポーリングできますが、これは実装が貧弱だと感じています。ディレクトリの「ポーリング」という考え方は嫌いですが、残念ながらSFTPを使用する必要があります。助言がありますか?

38
CB.

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();
    }
}

そして、それだけです。

47
Gijs Overvliet

SFTPはSSLを介したFTPではなく、SSHを介したFTPでもないことに注意してください。 SFTPサーバーのサポートには、JavaでのSSHDの実装が必要です。あなたの最善の策はApache SSHDです、

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

SFTPを使用したことはありませんが、基本的な機能であると聞きました。

4
ZZ Coder

上記で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();
  }
}
2
Richard Sandoz

SSHTools(j2ssh) を見てください。クライアントとサーバーが含まれます。

ただし、ディレクトリのポーリングはそれほど悪い考えではありません。j2sshを使用して独自のSFTPサーバーをセットアップするよりもおそらく信頼性が高いでしょう。この種のポーリングを行うアプリケーションの数は数え切れませんが、通常は非常にうまく機能します。

1
Bruce Blackshaw

完全を期すために、 SecureBlackbox ライブラリは、Java(Androidを含む)で独自のSSH/SFTPサーバーを作成するためのクラスを提供します。