Libreria SSH per Java

Qualcuno sa di una buona libreria per l’accesso SSH da Java.

Java Secure Channel (JSCH) è una libreria molto diffusa, utilizzata da maven, ant e eclipse. È open source con una licenza in stile BSD.

Aggiornamento: il progetto GSOC e il codice non sono attivi, ma questo è: https://github.com/hierynomus/sshj

hierynomus ha assunto il ruolo di manutentore dall’inizio del 2015. Ecco il link Github precedente, non più aggiornato:

https://github.com/shikhar/sshj


C’era un progetto GSOC:

http://code.google.com/p/commons-net-ssh/

La qualità del codice sembra essere migliore di JSch, che, nonostante un’implementazione completa e funzionante, manca di documentazione. La pagina del progetto indica una prossima versione beta, l’ultimo impegno per il repository è avvenuto a metà agosto.

Confronta le API:

http://code.google.com/p/commons-net-ssh/

SSHClient ssh = new SSHClient(); //ssh.useCompression(); ssh.loadKnownHosts(); ssh.connect("localhost"); try { ssh.authPublickey(System.getProperty("user.name")); new SCPDownloadClient(ssh).copy("ten", "/tmp"); } finally { ssh.disconnect(); } 

http://www.jcraft.com/jsch/

 Session session = null; Channel channel = null; try { JSch jsch = new JSch(); session = jsch.getSession(username, host, 22); java.util.Properties config = new java.util.Properties(); config.put("StrictHostKeyChecking", "no"); session.setConfig(config); session.setPassword(password); session.connect(); // exec 'scp -f rfile' remotely String command = "scp -f " + remoteFilename; channel = session.openChannel("exec"); ((ChannelExec) channel).setCommand(command); // get I/O streams for remote scp OutputStream out = channel.getOutputStream(); InputStream in = channel.getInputStream(); channel.connect(); byte[] buf = new byte[1024]; // send '\0' buf[0] = 0; out.write(buf, 0, 1); out.flush(); while (true) { int c = checkAck(in); if (c != 'C') { break; } // read '0644 ' in.read(buf, 0, 5); long filesize = 0L; while (true) { if (in.read(buf, 0, 1) < 0) { // error break; } if (buf[0] == ' ') { break; } filesize = filesize * 10L + (long) (buf[0] - '0'); } String file = null; for (int i = 0;; i++) { in.read(buf, i, 1); if (buf[i] == (byte) 0x0a) { file = new String(buf, 0, i); break; } } // send '\0' buf[0] = 0; out.write(buf, 0, 1); out.flush(); // read a content of lfile FileOutputStream fos = null; fos = new FileOutputStream(localFilename); int foo; while (true) { if (buf.length < filesize) { foo = buf.length; } else { foo = (int) filesize; } foo = in.read(buf, 0, foo); if (foo < 0) { // error break; } fos.write(buf, 0, foo); filesize -= foo; if (filesize == 0L) { break; } } fos.close(); fos = null; if (checkAck(in) != 0) { System.exit(0); } // send '\0' buf[0] = 0; out.write(buf, 0, 1); out.flush(); channel.disconnect(); session.disconnect(); } } catch (JSchException jsche) { System.err.println(jsche.getLocalizedMessage()); } catch (IOException ioe) { System.err.println(ioe.getLocalizedMessage()); } finally { channel.disconnect(); session.disconnect(); } } 

Ho appena scoperto sshj , che sembra avere un’API molto più concisa di JSCH (ma richiede Java 6). A questo punto, la documentazione è per la maggior parte degli esempi-in-the-repo, e di solito è sufficiente per me guardare altrove, ma mi sembra abbastanza buono per dargli una possibilità su un progetto che ho appena iniziato.

Dai uno sguardo all’SSHD recentemente rilasciato, basato sul progetto Apache MINA.

C’è una nuova versione di Jsch su github: https://github.com/vngx/vngx-jsch Alcuni dei miglioramenti includono: javadoc completo, prestazioni avanzate, gestione delle eccezioni migliorata e migliore aderenza alle specifiche RFC. Se si desidera contribuire in qualsiasi modo, si prega di aprire un problema o inviare una richiesta di pull.

Ho preso la risposta di miku e il codice di esempio di jsch. Ho quindi dovuto scaricare più file durante la sessione e conservare i timestamp originali . Questo è il mio codice di esempio su come farlo, probabilmente molte persone lo trovano utile. Ignora la funzione filenameHack () come se fosse il mio caso d’uso.

 package examples; import com.jcraft.jsch.*; import java.io.*; import java.util.*; public class ScpFrom2 { public static void main(String[] args) throws Exception { Map params = parseParams(args); if (params.isEmpty()) { System.err.println("usage: java ScpFrom2 " + " user=myid password=mypwd" + " host=myhost.com port=22" + " encoding=" + " \"remotefile1=/some/file.png\"" + " \"localfile1=file.png\"" + " \"remotefile2=/other/file.txt\"" + " \"localfile2=file.txt\"" ); return; } // default values if (params.get("port") == null) params.put("port", "22"); if (params.get("encoding") == null) params.put("encoding", "ISO-8859-1"); //"UTF-8" Session session = null; try { JSch jsch=new JSch(); session=jsch.getSession( params.get("user"), // myuserid params.get("host"), // my.server.com Integer.parseInt(params.get("port")) // 22 ); session.setPassword( params.get("password") ); session.setConfig("StrictHostKeyChecking", "no"); // do not prompt for server signature session.connect(); // this is exec command and string reply encoding String encoding = params.get("encoding"); int fileIdx=0; while(true) { fileIdx++; String remoteFile = params.get("remotefile"+fileIdx); String localFile = params.get("localfile"+fileIdx); if (remoteFile == null || remoteFile.equals("") || localFile == null || localFile.equals("") ) break; remoteFile = filenameHack(remoteFile); localFile = filenameHack(localFile); try { downloadFile(session, remoteFile, localFile, encoding); } catch (Exception ex) { ex.printStackTrace(); } } } catch(Exception ex) { ex.printStackTrace(); } finally { try{ session.disconnect(); } catch(Exception ex){} } } private static void downloadFile(Session session, String remoteFile, String localFile, String encoding) throws Exception { // send exec command: scp -p -f "/some/file.png" // -p = read file timestamps // -f = From remote to local String command = String.format("scp -p -f \"%s\"", remoteFile); System.console().printf("send command: %s%n", command); Channel channel=session.openChannel("exec"); ((ChannelExec)channel).setCommand(command.getBytes(encoding)); // get I/O streams for remote scp byte[] buf=new byte[32*1024]; OutputStream out=channel.getOutputStream(); InputStream in=channel.getInputStream(); channel.connect(); buf[0]=0; out.write(buf, 0, 1); out.flush(); // send '\0' // reply: T 0  0\n // times are in seconds, since 1970-01-01 00:00:00 UTC int c=checkAck(in); if(c!='T') throw new IOException("Invalid timestamp reply from server"); long tsModified = -1; // millis for(int idx=0; ; idx++){ in.read(buf, idx, 1); if(tsModified < 0 && buf[idx]==' ') { tsModified = Long.parseLong(new String(buf, 0, idx))*1000; } else if(buf[idx]=='\n') { break; } } buf[0]=0; out.write(buf, 0, 1); out.flush(); // send '\0' // reply: C0644  \n // length is given as a text "621873" bytes c=checkAck(in); if(c!='C') throw new IOException("Invalid filename reply from server"); in.read(buf, 0, 5); // read '0644 ' bytes long filesize=-1; for(int idx=0; ; idx++){ in.read(buf, idx, 1); if(buf[idx]==' ') { filesize = Long.parseLong(new String(buf, 0, idx)); break; } } // read remote filename String origFilename=null; for(int idx=0; ; idx++){ in.read(buf, idx, 1); if(buf[idx]=='\n') { origFilename=new String(buf, 0, idx, encoding); // UTF-8, ISO-8859-1 break; } } System.console().printf("size=%d, modified=%d, filename=%s%n" , filesize, tsModified, origFilename); buf[0]=0; out.write(buf, 0, 1); out.flush(); // send '\0' // read binary data, write to local file FileOutputStream fos = null; try { File file = new File(localFile); fos = new FileOutputStream(file); while(filesize > 0) { int read = Math.min(buf.length, (int)filesize); read=in.read(buf, 0, read); if(read < 0) throw new IOException("Reading data failed"); fos.write(buf, 0, read); filesize -= read; } fos.close(); // we must close file before updating timestamp fos = null; if (tsModified > 0) file.setLastModified(tsModified); } finally { try{ if (fos!=null) fos.close(); } catch(Exception ex){} } if(checkAck(in) != 0) return; buf[0]=0; out.write(buf, 0, 1); out.flush(); // send '\0' System.out.println("Binary data read"); } private static int checkAck(InputStream in) throws IOException { // b may be 0 for success // 1 for error, // 2 for fatal error, // -1 int b=in.read(); if(b==0) return b; else if(b==-1) return b; if(b==1 || b==2) { StringBuilder sb=new StringBuilder(); int c; do { c=in.read(); sb.append((char)c); } while(c!='\n'); throw new IOException(sb.toString()); } return b; } /** * Parse key=value pairs to hashmap. * @param args * @return */ private static Map parseParams(String[] args) throws Exception { Map params = new HashMap(); for(String keyval : args) { int idx = keyval.indexOf('='); params.put( keyval.substring(0, idx), keyval.substring(idx+1) ); } return params; } private static String filenameHack(String filename) { // It's difficult reliably pass unicode input parameters // from Java dos command line. // This dirty hack is my very own test use case. if (filename.contains("${filename1}")) filename = filename.replace("${filename1}", "Korilla ABC ÅÄÖ.txt"); else if (filename.contains("${filename2}")) filename = filename.replace("${filename2}", "test2 ABC ÅÄÖ.txt"); return filename; } } 

http://code.google.com/p/connectbot/ , Compilare src \ com \ trilead \ ssh2 su windows linux o android, può creare Local Port Forwarder o creare Dynamic Port Forwarder o altro