Foros Club Delphi

Foros Club Delphi (https://www.clubdelphi.com/foros/index.php)
-   Internet (https://www.clubdelphi.com/foros/forumdisplay.php?f=3)
-   -   ftp Upload / Download (Aplicación) (https://www.clubdelphi.com/foros/showthread.php?t=47376)

seoane 26-08-2007 01:53:37

ftp Upload / Download (Aplicación)
 
1 Archivos Adjunto(s)
Hace tiempo que no pongo código por aquí, al menos no código útil, así que vamos a ver que os parece esta pequeña herramienta.

Se trata de una aplicación de consola que permite subir o bajar un archivo de un servidor ftp, pero con la particularidad que lo cifra al subirlo y lo descifra al bajarlo. El cifrado que utiliza es AES de 256 bits, que nos permite usar contraseñas de hasta 32 caracteres, y es lo suficientemente seguro para que lo utilice el gobierno de los EE.UU.

Pero dije que este no era código inútil, así que vamos a buscarle alguna utilidad. La primera, y mas evidente, es subir un archivo:
Código:

ftpup --key=clave --local=archivo.txt --password=pass --user=user --remote=/archivo.txt --server=servidor.com
El comando anterior subiría el archivo "archivo.txt" al servidor "servidor.com", usando el nombre de usuario "user", el password "pass" y la clave de cifrado "clave". Hasta ahora nada que no se pudiera hacer con cualquier otro cliente de ftp, salvo el cifrado. Se me ocurre que puede servir para hacer una copia de seguridad de un documento privado, si lo combinamos, por ejemplo, con las tareas programadas de Windows.

Para bajarlo, nada mas sencillo que esto:
Código:

ftpup -d --key=clave --local=archivo.txt --password=pass --user=user --remote=/archivo.txt --server=servidor.com
Pero ya me conocéis, hay que buscar otro uso mas interesante a esto, darle otra vuelta de tuerca. Que os parece utilizarlo para guardar, y leer, anotaciones cifradas desde cualquier ordenador, sin dejar ningún rastro en el equipo. Pues vamos a probar, lo primero es decir que en el parámetro --local se puede usar un "." para decir que se utilice en vez de un archivo, la entrada y salida estándar. Sabiendo esto, podríamos hacer algo como esto:
Código:

echo Mensaje secreto | ftpup --key=clave --local=. --password=pass --user=user --remote=/archivo.txt --server=servidor.com
Y para leerlo algo como esto:
Código:

ftpup -d --key=clave --local=. --password=pass --user=user --remote=/archivo.txt --server=servidor.com
Y ya puestos, podríamos guardar cómodamente nuestras contraseñas en un archivo de texto, y desde nuestra casa subirlas al ftp. Así desde cualquier equipo en el que estemos podremos ver las contraseñas en pantalla, si necesidad de guardar ningún archivo en el disco.

Bueno, seguro que alguna utilidad mas se le puede sacar. Como siempre, criticas (constructivas) y sugerencias serán bien recibidas. Espero vuestros comentarios.

El código (es una aplicación de consola):
Código Delphi [-]
program ftpup;

{$APPTYPE CONSOLE}

uses
  Windows,
  SysUtils,
  Classes,
  WinInet,
  AES in 'AES.pas';

type
  EGetOptError = class(Exception);

var
  Options: record
    Download: Boolean; 
    Key: String;
    Local: String;
    Password: String;
    Port: Integer;
    Remote: String;
    Server: String;
    User: String;
  end;

function Starts(Sub, Str: String): Boolean;
begin
  Result:= SameText(Sub,Copy(Str,1,Length(sub)));
end;

procedure Getopt;
var
  i,j: Integer;
  Str: String;
begin
  FillChar(Options,Sizeof(Options),#0);
  for i:= 1 to ParamCount do
  begin
    Str:= ParamStr(i);
    if (Copy(Str,1,2) = '--') and (Length(Str) > 2) then
    begin
      if SameText(Str,'--Download') then
        Options.Download:= TRUE
      else if Starts('--Key=',Str) then
      begin
        if Options.Key = EmptyStr then
          Options.Key:= Copy(Str,Length('--Key=') + 1,MAXINT)
        else
          raise EGetOptError.Create(Str);
      end else if Starts('--Local=',Str) then
      begin
        if Options.Local = EmptyStr then
          Options.Local:= Copy(Str,Length('--Local=') + 1,MAXINT)
        else
          raise EGetOptError.Create(Str);
      end else if Starts('--Remote=',Str) then
      begin
        if Options.Remote = EmptyStr then
          Options.Remote:= Copy(Str,Length('--Remote=') + 1,MAXINT)
        else
          raise EGetOptError.Create(Str);
      end else if Starts('--Password=',Str) then
      begin
        if Options.Password = EmptyStr then
          Options.Password:= Copy(Str,Length('--Password=') + 1,MAXINT)
        else
          raise EGetOptError.Create(Str);
      end else if Starts('--Port=',Str) then
      begin
        if Options.Port = 0 then
          Options.Port:= StrToInt(Copy(Str,Length('--Port=') + 1,MAXINT))
        else
          raise EGetOptError.Create(Str);
      end else if Starts('--Server=',Str) then
      begin
        if Options.Server = EmptyStr then
          Options.Server:= Copy(Str,Length('--Server=') + 1,MAXINT)
        else
          raise EGetOptError.Create(Str);
      end else if Starts('--User=',Str) then
      begin
        if Options.User = EmptyStr then
          Options.User:= Copy(Str,Length('--User=') + 1,MAXINT)
        else
          raise EGetOptError.Create(Str);
      end else
        raise EGetOptError.Create(Str);
    end else if (Copy(Str,1,1) = '-') and (Length(Str) > 1) then
    begin
      Str:= UpperCase(Str);
      for j:= 2 to Length(Str) do
      begin
        if Str[j] = 'D' then
          Options.Download:= TRUE
        else
          raise EGetOptError.Create(Str);
      end;
    end else
    begin
      raise EGetOptError.Create(Str);
    end;
  end;
  if Options.Port = 0 then
    Options.Port:= 21;
end;

function ftpUpload(Stream: TStream; RemoteFile, Server: String; Port: Word;
  Username, Password: PChar; Key: TAESExpandedKey): Boolean;
var
  hNet: HINTERNET;
  hCon: HINTERNET;
  hFile: HINTERNET;
  Context: DWORD;
  Count: Cardinal;
  State, Temp: TAESState;
  Size: int64;
begin
  Result := FALSE;
  Context:= 0;
  hNet := InternetOpen('agent', INTERNET_OPEN_TYPE_PRECONFIG, nil, nil, 0);
  if (hNet <> nil) then
  begin
    hCon:= InternetConnect(hNet,PChar(Server),Port,Username,Password,
      INTERNET_SERVICE_FTP,0,Context);
    if (hCon <> nil) then
    begin
      hFile:= FtpOpenFile(hCon,PChar(RemoteFile),GENERIC_WRITE,
        FTP_TRANSFER_TYPE_BINARY,Context);
      if hFile <> nil then
      begin
        try
          if Stream is TFileStream then
            Size:= Stream.Size
          else
            Size:= -1;
          if InternetWriteFile(hFile,@Size,Sizeof(Size),Count) and
            (Count = Sizeof(Size))  then
          begin
            FillChar(Temp,Sizeof(Temp),#0);
            FillChar(State,Sizeof(State),#0);
            Count:= Stream.Read(State,Sizeof(State));
            while  Count > 0 do
            begin
              XORState(State,Temp);
              AESEncrypt(State,Key);
              if not InternetWriteFile(hFile,@State,Sizeof(State),Count) then
                break;
              Temp:= State;
              FillChar(State,Sizeof(State),#0);
              Count:= Stream.Read(State,Sizeof(State));
            end;
            Result:= Count = 0;
          end;
          if not Result then
            Writeln(SysErrorMessage(GetLastError));
        except
          On E: Exception do
            Writeln(E.Message);
        end;
        InternetCloseHandle(hFile);
      end else Writeln(SysErrorMessage(GetLastError));
      InternetCloseHandle(hCon);
    end else Writeln(SysErrorMessage(GetLastError));
    InternetCloseHandle(hNet);
  end else Writeln(SysErrorMessage(GetLastError));
end;

function ftpDownload(Stream: TStream; RemoteFile, Server: String; Port: Word;
  Username, Password: PChar; Key: TAESExpandedKey): Boolean;
var
  hNet: HINTERNET;
  hCon: HINTERNET;
  hFile: HINTERNET;
  Context: DWORD;
  Count: Cardinal;
  State, Temp, Ant: TAESState;
  Size: int64;
begin
  Result := FALSE;
  Context:= 0;
  hNet := InternetOpen('agent', INTERNET_OPEN_TYPE_PRECONFIG, nil, nil, 0);
  if (hNet <> nil) then
  begin
    hCon:= InternetConnect(hNet,PChar(Server),Port,Username,Password,
      INTERNET_SERVICE_FTP,0,Context);
    if (hCon <> nil) then
    begin
      hFile:= FtpOpenFile(hCon,PChar(RemoteFile),GENERIC_READ,
        FTP_TRANSFER_TYPE_BINARY,Context);
      if hFile <> nil then
      begin
        try
          if InternetReadFile(hFile,@Size,Sizeof(Size),Count) and
            (Count = Sizeof(Size))  then
          begin
            FillChar(Temp,Sizeof(Temp),#0);
            FillChar(State,Sizeof(State),#0);
            if InternetReadFile(hFile,@State,Sizeof(State),Count) then
            begin
              while  Count > 0 do
              begin
                Ant:= State;
                AESDecrypt(State,Key);
                XORState(State,Temp);
                if Stream.Write(State,Count) <> Integer(Count) then
                  break;
                Temp:= Ant;
                FillChar(State,Sizeof(State),#0);
                if not InternetReadFile(hFile,@State,Sizeof(State),Count) then
                  break;
              end;
              Result:= Count = 0;
            end;
            if (Stream is TFileStream) and (Stream.Size > Size)
              and (Size > 0 )then
              Stream.Size:= Size;
            if not Result then
              Writeln(SysErrorMessage(GetLastError));
          end;
        except
          On E: Exception do
            Writeln(E.Message);
        end;
        InternetCloseHandle(hFile);
      end else Writeln(SysErrorMessage(GetLastError));
      InternetCloseHandle(hCon);
    end else Writeln(SysErrorMessage(GetLastError));
    InternetCloseHandle(hNet);
  end else Writeln(SysErrorMessage(GetLastError));
end;

procedure Run;
var
  AESKey: TAESKey;
  ExKey: TAESExpandedKey;
  Stream: TStream;
begin
  FillChar(AESKey,Sizeof(AESKey),#0);
  if Length(Options.Key) > Sizeof(AESKey) then
    move(PChar(Options.Key)^,AESKey,Sizeof(AESKey))
  else
    move(PChar(Options.Key)^,AESKey,Length(Options.Key));
  AESExpandKey(ExKey,AESKey);
  if Options.Local = '.' then
  begin
    if Options.Download then
      Stream:= THandleStream.Create(GetStdHandle(STD_OUTPUT_HANDLE))
    else
      Stream:= THandleStream.Create(GetStdHandle(STD_INPUT_HANDLE));
  end else
  begin
    if Options.Download then
      Stream:= TFileStream.Create(Options.Local,fmCreate)
    else
      Stream:= TFileStream.Create(Options.Local,fmOpenRead or fmShareDenyNone);
  end;
  try
    if Options.Download then
      ftpDownload(Stream,Options.Remote,Options.Server,Options.Port,
        PChar(Options.User),PChar(Options.Password),ExKey)
    else
      ftpUpload(Stream,Options.Remote,Options.Server,Options.Port,
        PChar(Options.User),PChar(Options.Password),ExKey)
  finally
    Stream.Free;
  end;
end;

procedure Help;
begin
  Writeln;
  Writeln('Uso: ftpup [opciones]');
  Writeln('  --download -d');
  Writeln('    Baja el archivo del servidor');
  Writeln('  --key');
  Writeln('    Clave de cifrado, hasta 32 caracteres.');
  Writeln('  --local');
  Writeln('    Archivo local. Se puede utilizar un . para indicar la');
  Writeln('    entrada o salida estandar');
  Writeln('  --password');
  Writeln('    Clave de acceso al servidor ftp');
  Writeln('  --port');
  Writeln('    El puerto del ftp');
  Writeln('  --remote');
  Writeln('    Ruta del archivo remoto');
  Writeln('  --server');
  Writeln('    Direccion del servidor ftp');
  Writeln('  --user');
  Writeln('    Nombre de usuario del ftp');
  Writeln;
  Writeln('Ejemplos:');
  Writeln('  ftpup --key=clave --local=archivo.txt --password=pass ' +
    '--user=user --remote=/archivo.txt --server=servidor.com');
  Writeln('    Sube al servidor el archivo "archivo.txt".');
  Writeln('  ftpup -d --key=clave --local=. --password=pass ' +
    '--user=user --remote=/archivo.txt --server=servidor.com');
  Writeln('    Descarga el archivo "archivo.txt" y lo muestra por pantalla');
  Writeln;      
end;


begin
  try
    if ParamCount > 0 then
    begin
      GetOpt;
      Run;
    end else
      Help;
  except
    On E: EGetOptError do
    begin
      Writeln('Error en el parametro: ',E.Message);
      Help;
    end;
    On E: Exception do
      Writeln(E.Message);
  end;
end.

ArdiIIa 26-08-2007 01:57:13

A pesar de que lo has proclamado a los cuatro vientos, no estoy muy seguro de que no tengas nada contra las Indy...:D:D

seoane 26-08-2007 02:31:15

Cita:

Empezado por ArdiIIa (Mensaje 226149)
A pesar de que lo has proclamado a los cuatro vientos, no estoy muy seguro de que no tengas nada contra las Indy...:D:D

Yo contra Indy no tengo nada, pero a veces es como "matar moscas a cañonazos". Con lo simple que queda con wininet, además de pequeño (menos de 100 kb) y, sobre todo, utiliza la misma configuración del proxy que el internet explorer, ¿que mas se puede pedir?.

Todo esto no significa que cuando hay que "sacar el cañón para matar un mosca rebelde", por ejemplo para usar SSL, no utilice Indy.

A todo esto, ¿que te parece el programa Ardilla? :D

Casimiro Noteví 26-08-2007 03:04:48

Esto sí que es código "no inútil".
Tiene todo lo mejor que existe, las 3 "b": bueno, bonito y barato :)

seoane 26-08-2007 03:09:46

Cita:

Empezado por Casimiro Notevi (Mensaje 226156)
Esto sí que es código "no inútil".
Tiene todo lo mejor que existe, las 3 "b": bueno, bonito y barato :)

Eso, eso ... así me gusta Casimiro :D

eduarcol 26-08-2007 19:55:17

Esta muy bueno seoane, con tu permiso lo voy a comenzar a utilizar, voy a crear una especie de servicio para actualizar los sistemas, cuando lo termine lo coloco por aca a ver que tal

Jure 26-08-2007 21:32:31

Cita:

Empezado por seoane (Mensaje 226151)
... Con lo simple que queda con wininet, además de pequeño (menos de 100 kb) y, sobre todo, utiliza la misma configuración del proxy que el internet explorer, ¿que mas se puede pedir?....

Seoane es un excelente código, como muy bien dices: Simple, corto y práctico. Gracias por compartirlo ;)

seoane 26-08-2007 22:52:15

Cita:

Empezado por eduarcol (Mensaje 226215)
Esta muy bueno seoane, con tu permiso lo voy a comenzar a utilizar, voy a crear una especie de servicio para actualizar los sistemas, cuando lo termine lo coloco por aca a ver que tal

Excelente, ya nos contaras que tal te fue :)

Cita:

Empezado por Jure (Mensaje 226227)
Seoane es un excelente código, como muy bien dices: Simple, corto y práctico. Gracias por compartirlo ;)

Gracias :)

seoane 31-08-2007 13:31:21

Vamos a darle un poco de vida a este hilo ;)

Imaginar que que queremos descifrar un archivo cifrado con ftpup desde linux.

Nada nos impide usar algo como esto:
Código:

wine ftpup (Parametros)
Funcionaría, pero por qué no crear una aplicación propia para linux. Vamos a utilizar el compilador freepascal y el siguiente código:
Código Delphi [-]
program cifrar;

uses
  SysUtils,
  Classes,
  AES in 'AES.pas';

type
  EGetOptError = class(Exception);

var
  Options: record
    Decrypt: Boolean;
    Dest: String;
    Key: String;
    Source: String;    
  end;

function Starts(Sub, Str: String): Boolean;
begin
  Result:= SameText(Sub,Copy(Str,1,Length(sub)));
end;

procedure Getopt;
var
  i,j: Integer;
  Str: String;
begin
  FillChar(Options,Sizeof(Options),#0);
  for i:= 1 to ParamCount do
  begin
    Str:= ParamStr(i);
    if (Copy(Str,1,2) = '--') and (Length(Str) > 2) then
    begin
      if SameText(Str,'--Decrypt') then
        Options.Decrypt:= TRUE
      else if Starts('--Key=',Str) then
      begin
        if Options.Key = EmptyStr then
          Options.Key:= Copy(Str,Length('--Key=') + 1,MAXINT)
        else
          raise EGetOptError.Create(Str);
      end else if Starts('--Source=',Str) then
      begin
        if Options.Source = EmptyStr then
          Options.Source:= Copy(Str,Length('--Source=') + 1,MAXINT)
        else
          raise EGetOptError.Create(Str);
      end else if Starts('--Dest=',Str) then
      begin
        if Options.Dest = EmptyStr then
          Options.Dest:= Copy(Str,Length('--Dest=') + 1,MAXINT)
        else
          raise EGetOptError.Create(Str);
      end;
    end else if (Copy(Str,1,1) = '-') and (Length(Str) > 1) then
    begin
      Str:= UpperCase(Str);
      for j:= 2 to Length(Str) do
      begin
        if Str[j] = 'D' then
          Options.Decrypt:= TRUE
        else
          raise EGetOptError.Create(Str);
      end;
    end else
    begin
      raise EGetOptError.Create(Str);
    end;
  end;
end;


procedure Run;
var
  AESKey: TAESKey;
  ExKey: TAESExpandedKey;
  Src, Dst: TStream;
  Size: int64;
  IV: TAESState;
begin
  FillChar(AESKey,Sizeof(AESKey),#0);
  if Length(Options.Key) > Sizeof(AESKey) then
    move(PChar(Options.Key)^,AESKey,Sizeof(AESKey))
  else
    move(PChar(Options.Key)^,AESKey,Length(Options.Key));
  AESExpandKey(ExKey,AESKey);
  FillChar(IV,Sizeof(IV),#0);  
  Src:= TFileStream.Create(Options.Source,fmOpenRead or fmShareDenyNone);
  try
    Dst:= TFileStream.Create(Options.Dest,fmCreate); 
    try 
      if Options.Decrypt then
      begin
        if Src.Read(Size,Sizeof(Size)) = Sizeof(Size) then
        begin
          AESDecryptStreamCBC(IV,Src,Dst,ExKey);
          if (Size > 0) and (Src.Size > Size) then
            Dst.Size:= Size;
        end;
      end else
      begin
        Size:= Src.Size;
        Dst.Write(Size,Sizeof(Size));
        AESEncryptStreamCBC(IV,Src,Dst,ExKey);
      end;
    finally
      Dst.Free;
    end;
  finally
    Src.Free;
  end;
end;

procedure Help;
begin
  Writeln;
  Writeln('Uso: cifrar [opciones]');
  Writeln('  --decrypt -d');
  Writeln('    Descifrar (Cifrar es la opcion por defecto)');
  Writeln('  --key');
  Writeln('    Clave de cifrado, hasta 32 caracteres.');
  Writeln('  --source');
  Writeln('    Archivo de origen');
  Writeln('  --dest');
  Writeln('    Archivo de destino.');  
  Writeln;
  Writeln('Ejemplos:');
  Writeln('  cifrar --key=clave --source=archivo.txt --dest=archivo.bin');
  Writeln('  cifrar -d --key=clave --source=archivo.txt --dest=archivo.bin');
  Writeln;      
end;


begin
  try
    if ParamCount > 0 then
    begin
      GetOpt;
      Run;
    end else
      Help;
  except
    On E: EGetOptError do
    begin
      Writeln('Error en el parametro: ',E.Message);
      Help;
    end;
    On E: Exception do
      Writeln(E.Message);
  end;
end.
Y lo compilamos de la siguiente manera:
Código:

fpc -Sd cifrar.pas
El programa resultante permite cifrar y descifrar un archivo utilizando AES 256, siguiendo el mismo formato que el programa ftpup.

Por ejemplo, así cifra:
Código:

./cifrar --source=archivo.txt --dest=archivo.bin --key=clave
Y así descifra:
Código:

./cifrar -d --source=archivo.bin --dest=archivo.txt --key=clave
PD: La unit AES.pas la puedes conseguir aquí

Casimiro Noteví 31-08-2007 15:09:12

Estupendo, seoane, lo pruebo en cuanto tenga un momento. (*)






(*) Cuando reciba mi nuevo monitor :)

seoane 31-08-2007 15:32:15

Cita:

Empezado por Casimiro Notevi (Mensaje 227351)
Estupendo, seoane, lo pruebo en cuanto tenga un momento.

Sera un placer que me digas tu opinión :)


PD: Usa el código que hay ahora, el que puse al principio tenia algunos errores.

Casimiro Noteví 06-03-2008 22:51:02

Cita:

Empezado por seoane (Mensaje 227357)
Sera un placer que me digas tu opinión :)

PD: Usa el código que hay ahora, el que puse al principio tenia algunos errores.

Un poquito tarde, pero más vale tarde que nunca :D

Lo he probado en mi ubuntu, compilación sin ningún error, el programita funciona perfecto, sin problema alguno y muy rápido.
Lo estoy usando para hacer copias encriptadas y luego subirlas por ftp con tu ftpup.

seoane 06-03-2008 23:13:14

Excelente :D me encanta que la gente use los programas que hago.

Para saber mas sobre el tema, 4 entradas de mi pagina que tratan sobre AES:

BlackDaemon 08-03-2008 03:03:47

Muchas gracias maestro, algún día entenderé el código que escribes :D
Por el momento probando los binarios, esta muy chiquito :)

Por cierto, no puedes subir un directorio ?


saludos!

seoane 08-03-2008 15:07:11

Cita:

Empezado por BlackDaemon (Mensaje 271688)
Por cierto, no puedes subir un directorio ?

:D Para eso esta el Winrar, Winzip, tar, 7z, .... :D

eduarcol 08-03-2008 15:24:37

Cita:

Empezado por seoane (Mensaje 271736)
:D Para eso esta el Winrar, Winzip, tar, 7z, .... :D

Mira como seras de malo, ayuda al amigo con codigo no con ideas :D:D

Hablando en serio, en mis poquitisimos tiempos libres estoy creando un programa que suba un respaldo cifrado con el codigo del amigo Seoane asi como lo dije en su momento que lo iba a hacer, pero de a poquito a poquito ya he llegado mas o menos lejos, cuando lo termine y cuando Emilio me reactive el dominio lo subo para que lo vean.

Este es el codigo un poquito chapucero que me he inventado para comprimir un directorio:

Código Delphi [-]
cComprimir := 'LHA a ' + RutaRespaldo + ' ' + Ruta + '\*.*';
        winexec(PAnsiChar(cComprimir), SW_NORMAL);

Tienes que descargarte el LHA, RutaRespaldo es la ruta y nombre completo del archivo comprimido, y Ruta es el directorio que quieres comprimir.


La franja horaria es GMT +2. Ahora son las 19:51:43.

Powered by vBulletin® Version 3.6.8
Copyright ©2000 - 2026, Jelsoft Enterprises Ltd.
Traducción al castellano por el equipo de moderadores del Club Delphi