jueves, 3 de abril de 2008

Transferencia de archivos con SCP y JAVA

Este es un tutorial que permite a programadores en Java la transmisión de archivos mediante Conexión SCP (Protocolo de copia segura). A continuación vamos a describir los pasos para lograrlo.

Primero: Obtener la librería j2ssh

j2ssh-core.jar

URL de descarga: http://sourceforge.net/project/showfiles.php?group_id=60894

Segundo: Crear una clase para la conexión

Es conveniente crear una clase que se encargue de la conexión y transmisión de archivos mediante j2ssh.


package cl.miapp.scp;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ConnectException;

import com.sshtools.j2ssh.ScpClient;
import com.sshtools.j2ssh.SshClient;
import com.sshtools.j2ssh.authentication.PublicKeyAuthenticationClient;
import com.sshtools.j2ssh.transport.IgnoreHostKeyVerification;
import com.sshtools.j2ssh.transport.publickey.SshPrivateKey;
import com.sshtools.j2ssh.transport.publickey.SshPrivateKeyFile;

public class SecureScpBanco {
private SshClient ssh;
private PublicKeyAuthenticationClient pass;
private String hostname;
private String user;
public SecureScpBanco(String hostname, String login){
ssh = new SshClient();
this.hostname = hostname;
user = login;
pass = new PublicKeyAuthenticationClient();
}
public void sendFile(InputStream f, long size, String nombreLocal,
String nombreRemoto) throws ConnectException{
try {
ssh.connect(hostname, new IgnoreHostKeyVerification());
pass.setUsername(user);
String keyfile = "/ruta/archivo/claveprivada/id_rsa";
String passphrase = "";
SshPrivateKeyFile file = SshPrivateKeyFile.parse(new File(keyfile));
SshPrivateKey key = file.toPrivateKey(passphrase);
pass.setKey(key);
int ret = ssh.authenticate(pass);
if(ret == 4) {
ScpClient scpClient = ssh.openScpClient();
scpClient.put(f, size, nombreLocal, nombreRemoto);
ssh.disconnect();
} else throw new ConnectException("Error en la autenticacion de usuario");
} catch(IOException e) {
e.printStackTrace();
throw new ConnectException("No se pudo conectar al Servidor = " + hostname);
}
}

public boolean getFile(OutputStream destino, String ruta_origen)
throws ConnectException{
try{
ssh.connect(hostname, new IgnoreHostKeyVerification());
pass.setUsername(user);
String keyfile = "/ruta/archivo/claveprivada/id_rsa";
String passphrase = "";
SshPrivateKeyFile file = SshPrivateKeyFile.parse(new File(keyfile));
SshPrivateKey key = file.toPrivateKey(passphrase);
pass.setKey(key);
int ret = ssh.authenticate(pass);
if(ret == 4){
ScpClient scpClient = ssh.openScpClient();
InputStream stream = scpClient.get(ruta_origen);
BufferedInputStream bis = new BufferedInputStream(stream);
BufferedOutputStream bos = new BufferedOutputStream(destino);
byte data[] = new byte[8024];
int contador;
while((contador = bis.read(data, 0, 8024)) != -1)
bos.write(data, 0, contador);
ssh.disconnect();
return true;
} else{
throw new ConnectException("Error en la autenticacion de usuario");
}
}catch(IOException e){
e.printStackTrace();
}catch(Throwable ex){
ex.printStackTrace();
}
throw new ConnectException("Error de Entrada/Salida");
}
}


Tercero: Crear el Main

Con la clase creada anteriormente se simplifican mucho las cosas, ahora sólo hay que utilizarla de la siguiente forma:


import java.io.FileOutputStream;
import java.io.File;
import java.io.FileInputStream;
import cl.miapp.scp.SecureScpBanco;

public class TestMain{
public static void main(String []args) throws Exception {
FileOutputStream out = new FileOutputStream("nuevoArchivoLocal.txt");
SecureScpBanco scpBco = new SecureScpBanco("miHostRemoto", "miUsuarioRemoto");
if(scpBco.getFile(out, "/path/miArchivoRemoto.txt")){//ok
System.out.println("Archivo rescatado");
}else{
System.out.println("Error");
}
File fileIn = new File("ArchivoAEnviar.txt");
FileInputStream in = new FileInputStream(fileIn);
scpBco.sendFile(in,fileIn.length(),"archivoOrigen.txt", "archivoDestino.txt");
}
}


Cuarto: Generar las claves pública y privada

Finalmente hay que obtener el archivo de la clave privada que se utiliza, para esto hay que acceder a la máquina remota y tipear el siguiente comando (en linux):

# ssh-keygen -t rsa
Generating public/private rsa key pair.
Enter file in which to save the key (/home/usrcnv/.ssh/id_rsa):
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /home/usrcnv/.ssh/id_rsa.
Your public key has been saved in /home/usrcnv/.ssh/id_rsa.pub.

No ingresar “passphrase”.
Este comando crea los archivos de clave pública “id_rsa” y de clave privada “id_rsa.pub”.

El archivo “id_rsa.pub” se agrega al archivo que contiene las claves autorizadas “authorized_keys” ubicado en $HOME/.ssh/authorized_keys.

Para agregar la clave se tipea el siguente comando:

# cat id_rsa.pub >> authorized_keys

Finalmente copiar el archivo $HOME/.ssh/id_rsa en un directorio local.

Ahora sólo falta ejecutarlo.

10 comentarios:

Unknown dijo...

Hola :)
Revise como hiciste, pero no puedo bajarme el jar :'(

Rim@ster dijo...

Ana

Puedes descargar SSHTools desde

http://sourceforge.net/projects/sshtools

El jar j2ssh-core-version.jar se encuentra dentro de estas utilidades

Fernando dijo...

Hola !

Esto pinta bien, sin embargo me gustaria saber como puedo autenticar la conexion con password (que no viene en tu ejemplo)..

Saludos.
Fernando

Rim@ster dijo...

Para autenticar con password sería algo así:

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ConnectException;

import com.sshtools.j2ssh.ScpClient;
import com.sshtools.j2ssh.SshClient;
import com.sshtools.j2ssh.authentication.PasswordAuthenticationClient;
import com.sshtools.j2ssh.transport.IgnoreHostKeyVerification;

public class SecureScpTest {
private SshClient ssh;
private String hostname;
private PasswordAuthenticationClient pass;
public SecureScpTest(String hostname, String login, String passwd){
ssh = new SshClient();
this.hostname = hostname;
this.pass = new PasswordAuthenticationClient();
this.pass.setUsername(login);
this.pass.setPassword(passwd);
}
public void sendFile(InputStream f, long size, String nombreLocal,
String nombreRemoto) throws ConnectException{
try {
ssh.connect(hostname, new IgnoreHostKeyVerification());
int ret = ssh.authenticate(pass);
if(ret == 4) {
ScpClient scpClient = ssh.openScpClient();
scpClient.put(f, size, nombreLocal, nombreRemoto);
ssh.disconnect();
} else throw new ConnectException("Error en la autenticacion de usuario");
} catch(IOException e) {
e.printStackTrace();
throw new ConnectException("No se pudo conectar al Servidor = " + hostname);
}
}

public boolean getFile(OutputStream destino, String ruta_origen)
throws ConnectException{
try{
ssh.connect(hostname, new IgnoreHostKeyVerification());
int ret = ssh.authenticate(pass);
if(ret == 4){
ScpClient scpClient = ssh.openScpClient();
InputStream stream = scpClient.get(ruta_origen);
BufferedInputStream bis = new BufferedInputStream(stream);
BufferedOutputStream bos = new BufferedOutputStream(destino);
byte data[] = new byte[8024];
int contador;
while((contador = bis.read(data, 0, 8024)) != -1)
bos.write(data, 0, contador);
ssh.disconnect();
bis.close();
bos.close();
return true;
} else{
throw new ConnectException("Error en la autenticacion de usuario");
}
}catch(IOException e){
e.printStackTrace();
}catch(Throwable ex){
ex.printStackTrace();
}
throw new ConnectException("Error de Entrada/Salida");
}
public static void main(String []args) throws Exception {
FileOutputStream out = new FileOutputStream("D:/nuevoArchivoLocal.txt");
SecureScpTest scpBco = new SecureScpTest("miServidor", "miLogin", "miPassword");
if(scpBco.getFile(out, "/tmp/ejemplo.txt")){//ok
System.out.println("Archivo rescatado");
}else{
System.out.println("Error");
}
out.close();
File fileIn = new File("D:/nuevoArchivoLocal.txt");
FileInputStream in = new FileInputStream(fileIn);
scpBco.sendFile(in,fileIn.length(),"nuevoArchivoLocal.txt", "ejemplo2.txt");
in.close();
}
}

Unknown dijo...

Hola... muy bueno este aporte, me ha ayudado bastante. Lo que no veo es donde setea el puerto, queda acotado a solo usarlo con purto 22?..

Saludos..

Rim@ster dijo...

Debes utilizar el siguiente método para la conexión:

public void connect(String hostname, int port, HostKeyVerification hosts)

Por ejemplo si es el puerto 23, cambiar donde dice:
ssh.connect(hostname, new IgnoreHostKeyVerification());

por:
ssh.connect(hostname, 23, new IgnoreHostKeyVerification());

Saludos

Unknown dijo...

Gracias por responder, voy a probarlo asi.
Otra duda, me da este error:

Exception in thread "main" java.lang.NoClassDefFoundError: org/apache/commons/logging/LogFactory
at com.sshtools.j2ssh.SshClient.(Unknown Source)
at ar.edu.logica.Scp.(Scp.java:32)
at ar.edu.vista.Presentacion.creaVentana(Presentacion.java:128)
at ar.edu.vista.Main.main(Main.java:21)

Segun la excepcion falta la clase SshClient(). Descargue el el sshtools, agregue toda la carpeta a mi pryecto en Netbeans, pero me da este error...

Que podria ser??..

Rim@ster dijo...

El zip descargable j2ssh-.zip contiene la librería dependecia commons-logging.jar en la carpeta lib. Debes agregarla al classpath.

Saludos

Unknown dijo...

Muchas gracias funciona perfecto.. Saludos..

Unknown dijo...

Hola. Necesitaria ejecutar un comando en el servidor al que me estoy conectando, ejecutarlo desde la maquina cliente. Es decir, en el cliente debo mostrar una lista de archivos que hay en un determinado directorio para poder seleccionar uno o varios y descargarlos al cliente, pensaba en que la aplicacion ejecute un "ls" por ejemplo.
Hay alguna forma de hacer esto con sshtools?.