web-dev-qa-db-fra.com

Java Bibliothèque du serveur SFTP?

Existe-t-il une bibliothèque Java Java qui peut être utilisée pour implémenter un serveur SFTP ?

J'essaie de recevoir des fichiers via SFTP, mais je n'arrive pas à trouver d'implémentation d'un serveur SFTP. J'ai trouvé des bibliothèques FTP/SFTP/FTPS client et des bibliothèques de serveur FTP/FTPS, mais aucune pour un serveur pour SFTP.

Pour clarifier, j'essaie de recevoir fichiers via SFTP. Ne pas "récupérer" ou "mettre" des fichiers de mon application vers un autre serveur existant.

En ce moment, mon application permet aux utilisateurs de se connecter au serveur SFTP linux local, de supprimer les fichiers, puis mon application interroge le répertoire, mais je pense que c'est une mauvaise mise en œuvre; Je déteste l'idée de "polling" des répertoires, mais malheureusement, ils DOIVENT utiliser SFTP. Aucune suggestion?

38
CB.

Comment configurer un serveur SFTP en utilisant Apache Mina SSHD :

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

Et c'est tout.

47
Gijs Overvliet

Veuillez noter que SFTP n'est pas FTP sur SSL, ni FTP sur SSH. La prise en charge du serveur SFTP nécessite une implémentation de SSHD en Java. Votre meilleur pari est Apache SSHD,

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

Je n'ai jamais utilisé le SFTP mais j'ai entendu que c'était basique mais fonctionnel.

4
ZZ Coder

J'ai essayé de faire MINA 0.10.1 sur Windows avec ce qui précède et corrigé certains problèmes, en plus j'ai besoin d'une meilleure authentification et d'un support PK (toujours pas recommandé pour une utilisation en production):

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

Jetez un œil à SSHTools (j2ssh) . Il comprend un client et un serveur.

Cependant, interroger un répertoire n'est pas une si mauvaise idée - c'est probablement beaucoup plus fiable que de configurer votre propre serveur SFTP en utilisant j2ssh. J'ai perdu le compte du nombre d'applications que j'ai rencontrées qui effectuent ce type d'interrogation, et cela fonctionne généralement très bien.

1
Bruce Blackshaw

Juste pour être complet - la bibliothèque SecureBlackbox , que nous maintenons, propose des classes pour créer votre propre serveur SSH/SFTP dans Java (y compris Android).