2010-06-19 27 views
32

¿Existe una biblioteca Java que se pueda usar para implementar un servidor SFTP?biblioteca de servidor Java SFTP?

Estoy tratando de recibir archivos a través de SFTP, pero parece que no puedo encontrar ninguna implementación de un servidor SFTP. He encontrado FTP/SFTP/FTPS cliente bibliotecas y bibliotecas de servidor FTP/FTPS, pero ninguna para un servidor para SFTP.

Para aclarar, estoy tratando de recibir archivos a través de SFTP. No puedo "obtener" o "poner" archivos de mi aplicación en otro servidor existente.

Ahora mi aplicación permite a los usuarios conectarse al servidor SFTP linux local, soltar los archivos, y luego mi aplicación sondea el directorio, pero creo que esta es una implementación deficiente; Odio la idea de directorios de "sondeo", pero desafortunadamente TIENEN que usar SFTP. ¿Alguna sugerencia?

Respuesta

40

¿Cómo configurar un servidor SFTP utilizando 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(); 
    } 
} 

Y eso es todo.

+0

Esto crea un servidor SSH al que la mayoría de los clientes modernos se negarán a conectarse :-(Ver http://stackoverflow.com/questions/33690689/no-matching-host-key-key-type-found-apache-mina-sftp-server – Rich

0
+0

El enlace que ha proporcionado no es una biblioteca de Java, es un producto independiente. Además, no parece usar SFTP, sino FTPS. ¿Leyó algo sobre este producto, o simplemente eligió el segundo enlace cuando buscó en Google "ftp seguro de Java"? –

+5

Mis disculpas, fui a través de un enlace que decía que javasecureftpd estaba implementando SFTP y pensé que sí. De todos modos, no tienes que ser malo, solo estaba tratando de ayudarte. – pakore

-1

le sugiero que eche un vistazo a Apache Camel. Es compatible con FTP, FTPS y SFTP

+1

Aquí hay una cita directa del sitio web de Apache Camel que ha vinculado: "Este componente proporciona acceso a sistemas de archivos remotos a través de los protocolos FTP y SFTP". El punto clave es "acceso a sistemas de archivos remotos", esto no le permite alojar su propio servidor SFTP. –

4

Tenga en cuenta que SFTP no es FTP sobre SSL, ni FTP sobre SSH. El soporte de servidor SFTP requiere una implementación de SSHD en Java. Su mejor apuesta es Apache sshd,

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

nunca he usado el SFTP pero he oído que es básico pero funcional.

+0

Buen punto. La terminología es francamente confusa. Mucha gente piensa que SFTP es "Secure FTP", o una versión de FTP que se ejecuta sobre SSL o SSH, o algo así (y no lo es). Ojalá lo hubieran llamado algo completamente diferente. – hotshot309

+0

El enlace está muerto por cierto – Tommy

1

Eche un vistazo a SSHTools (j2ssh). Incluye un cliente y servidor.

Sin embargo, el sondeo de un directorio no es una mala idea, es probablemente mucho más confiable que configurar su propio servidor SFTP usando j2ssh. He perdido la cuenta de la cantidad de aplicaciones que he encontrado que hacen este tipo de sondeo, y generalmente funciona bastante bien.

+0

No sé si lo hizo en el pasado, pero ahora no parece ofrecer un servidor (de código abierto). Tal vez se haya trasladado a su oferta comercial. –

0

estoy usando JFTP http://j-ftp.sourceforge.net/ extracto de jftp.jar de JFTP -. * TGZ/JFTP/dist el único problema - ponen clases de Apache en el interior hay tarro (así que tengo que quitar común httpclient, log4j paquetes manualmente para evitar dependencias en conflicto)

+0

¿Por qué votar abajo? –

+1

Probablemente porque la pregunta era sobre un servidor SFTP, no un cliente. – FelixM

1

Para completar, nuestra biblioteca SecureBlackbox (edición Java) ofrece clases para crear su propio servidor SSH/SFTP en Java (incluido Android).

+3

es comercial. – Alvins

2

He intentado hacer MINA 0.10.1 en Windows con lo anterior y han solucionado algunos problemas, además de que necesitan una mejor autentificación y el apoyo PK (todavía no se recomienda para su uso en producción):

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