Cifrar texto con AES-256

Siguiendo con el tema del cifrado AES-256, del que ya hable aquí y aquí, vamos a ver un par de funciones que nos permitirán cifrar y descifrar texto de una manera rápida y sencilla, mientras mantenemos toda la potencia de este algoritmo de cifrado. Para esto vamos a combinar el código que podemos encontrar aquí, con este otro que publique aquí y que nos permitirá representar el texto cifrado con caracteres legibles utilizando la codificación base64.

Lo primero es lo primero, vamos a ver como cifrar el texto:

// Estas dos "units" las puedes encontrar en los enlaces que he puesto
uses AES, Base64;
 
function Cifrar(Str,Clave: String): String;
var
  Src: TStringStream;
  Dst: TMemoryStream;
  Size: Integer;
  Key: TAESKey;
  ExpandedKey: TAESExpandedKey;
begin
  Result:= EmptyStr;
  Src:= TStringStream.Create(Str);
  try
    Dst:= TMemoryStream.Create;
    try
      // Preparamos la clave, lo ideal es que tenga 32 caracteres
      FillChar(Key,Sizeof(Key),#0);
      if Length(Clave) > Sizeof(Key) then
        move(PChar(Clave)^,Key,Sizeof(key))
      else
        move(PChar(Clave)^,Key,Length(Clave));
      AEsExpandKey(ExpandedKey,Key);
      // Guardamos el tamaño del texto original
      Size:= Src.Size;
      Dst.WriteBuffer(Size,Sizeof(Size));
      // Ciframos el texto
      AESEncryptStreamECB(Src,Dst,ExpandedKey);
      // Lo codificamos a base64
      Result:= BinToStr(Dst.Memory,Dst.Size);
    finally
      Dst.Free;
    end;
  finally
    Src.Free;
  end;
end;

Y ahora vamos a ver como descifrarlo:

function Descifrar(Str,Clave: String): String;
var
  Src: TMemoryStream;
  Dst: TStringStream;
  Size: Integer;
  Key: TAESKey;
  ExpandedKey: TAESExpandedKey;
begin
  Result:= EmptyStr;
  Src:= TMemoryStream.Create;
  try
    Dst:= TStringStream.Create(Str);
    try
      StrToStream(Str,Src);
      Src.Position:= 0;
      FillChar(Key,Sizeof(Key),#0);
      if Length(Clave) > Sizeof(Key) then
        move(PChar(Clave)^,Key,Sizeof(key))
      else
        move(PChar(Clave)^,Key,Length(Clave));
      AESExpandKey(ExpandedKey,Key);
      // Leemos el tamaño del texto
      Src.ReadBuffer(Size,Sizeof(Size));
      AESDecryptStreamECB(Src,Dst,ExpandedKey);
      Dst.Size:= Size;
      Result:= Dst.DataString;
    finally
      Dst.Free;
    end;
  finally
    Src.Free;
  end;
end;

Para terminar, un pequeño ejemplo de como funciona:

  • Ciframos el texto "Hola mundo": Str:= Cifrar("Hola mundo",'1234567890');
  • Ahora la variable Str contiene el texto: CgAAADpn2TqC3VGqJmIQdPzgAaA=
  • Desciframos el texto anterior:Str:= Descifrar("CgAAADpn2TqC3VGqJmIQdPzgAaA=",'1234567890');
  • Ahora la variable Str vuelve a contener el texto: "Hola mundo"

Enlaces relacionados:
http://delphi.jmrds.com/?q=node/31
http://delphi.jmrds.com/?q=node/43

Para saber mas sobre AES:
http://es.wikipedia.org/wiki/AES
http://en.wikipedia.org/wiki/Advanced_Encryption_Standard
http://csrc.nist.gov/CryptoToolkit/aes/rijndael/

Excelente todo el trabajo que

Excelente todo el trabajo que realizaste con AES y el cifrado. De verdad te FELICITO!!!!

Una consulta... podrias

Una consulta...

podrias explicarme q hace esta parte del codigo, he revisado la ayuda pero no entiendo exactamente lo que hace la rutina Move

       if Length(Clave) > Sizeof(Key) then
        move(PChar(Clave)^,Key,Sizeof(key))
      else
        move(PChar(Clave)^,Key,Length(Clave));

Tambien lo he probado en Delphi 2009 pero no funciona como deberia, debe ser por el cambio q hay con los strings, ¿la unit Base64 es compatible con este delphi?.

De antemano te doy las gracias.

Básicamente copia los bytes

Básicamente copia los bytes que componen la variable clave en la variable key. No se como funcionara en Delphi 2009, en las versiones anteriores cada carácter de una cadena ocupaba un solo byte, pero he oído que en delphi 2009 podría no ser así. No he trabajado todavía en delphi 2009 así que no te puedo ayudar en eso.

Espero y no te molestes pero

Espero y no te molestes pero he cambiado algo al codigo para que sea compatible con Delphi 2009.

Añadi la rutina KeyToAESKey, comun a las existentes, corrigiendo el error que se producia debido al tamaño que ocupa un caracter en el nuevo Delphi, ahora es 2 bytes, este cambio en el tamaño hace que la longitud de la clave varie, ahora seria de 16 caracteres como maximo.

Si me preguntan porque coloque SizeKey := Length(Key) * SizeOf(Char); en lugar de SizeKey := SizeOf(Key); tengo dos razones:
1. El tamaño depende de lo que ocupa un Char.
2. En la ayuda de delphi dice que es lo mismo poner cualquiera, pero, en la practica me da otro resultado poner la segunda opcion, para cadenas de 2 o mas caracteres siempre me da SizeOf(Key) = 4, porque, eso si no lo se.

Tambien cambie la unit Base64 por Mime, esto porque al parecer la unit Base64 no trabaja muy bien con el nuevo string, esta unit se puede descargar de la pagina MIME Base64 Encoding/Decoding, el enlace de descarga se encuentra al final de dicha pagina, solo tienen que registrarse para descargarla, la unit es de uso libre segun tengo entendido.

Otra cosa... no lo he probado con otro delphi ya que no dispongo de otra version, pero, el tamaño que ocupa un string cambia de acuerdo a lo que ocupe un Char que en delphi anteriores seria 1, asi que teoricamente no deberia haber problema.

uses
  Classes, SysUtils;
 
function StrEncrypt(Str, Key: string): string;
 
function StrDecrypt(Str, Key: string): string;
 
implementation
 
uses
  AES, Mime;
 
  // Solo se usa como maximo los primeros 16 caracteres de Key
  procedure KeyToAESKey(var Key: string; var AESKey: TAESKey);
  var
    SizeKey, SizeAESKey: Integer;
  begin
    FillChar(AESKey, Sizeof(TAESKey), 0);
 
    SizeKey := Length(Key) * SizeOf(Char);
    SizeAESKey := SizeOf(TAESKey);
 
    if SizeKey > SizeAESKey then
      Move(PChar(Key)^, AESKey, SizeAESKey)
    else
      Move(PChar(Key)^, AESKey, SizeKey);
  end;
 
function StrEncrypt(Str, Key: string): string;
var
  Src: TStringStream;
  Dst: TMemoryStream;
  Size: Integer;
  AESKey: TAESKey;
  ExpandedKey: TAESExpandedKey;
begin
  Result := EmptyStr;
  Src := TStringStream.Create(Str);
  try
    Dst := TMemoryStream.Create;
    try
      KeyToAESKey(Key, AESKey);
      AESExpandKey(ExpandedKey, AESKey);
      // Guardamos el tamaño del texto original
      Size := Src.Size;
      Dst.WriteBuffer(Size, Sizeof(Size));
      // Ciframos el texto
      AESEncryptStreamECB(Src, Dst, ExpandedKey);{}
      // Lo codificamos a base64
      Result := MIMEEncode(Dst.Memory, Dst.Size);{
      Result := BinToStr(Dst.Memory, Dst.Size);{}
    finally
      Dst.Free;
    end;
  finally
    Src.Free;
  end;
end;
 
function StrDecrypt(Str, Key: string): string;
var
  Src: TMemoryStream;
  Dst: TStringStream;
  Size: Integer;
  AESKey: TAESKey;
  ExpandedKey: TAESExpandedKey;
  ADiscard: Integer;
begin
  Result := EmptyStr;
  Src := TMemoryStream.Create;
  try
    Dst := TStringStream.Create(Str);
    try
      KeyToAESKey(Key, AESKey);
      AESExpandKey(ExpandedKey, AESKey);{}
      Src := MIMEDeCode(Str, ADiscard);{
      StrToStream(Str, Src);{}
      Src.Position := 0;
      // Leemos el tamaño del texto
      Src.ReadBuffer(Size, Sizeof(Size));
      AESDecryptStreamECB(Src, Dst, ExpandedKey);
      Dst.Size := Size;
      Result := Dst.DataString;
    finally
      Dst.Free;
    end;
  finally
    Src.Free;
  end;
end;
 
end.

No estoy molesto, al

No estoy molesto, al contrario, estoy encantado de que publiques aquí tu código.

Por cierto la explicación de que Sizeof(Key) sea igual a 4 es que Key, como todas los Strings en delphi, es un puntero y el tamaño de un puntero de 32bits es de 4 bytes.

No tengo pensado trabajar próximamente con Delphi 9 pero me temo que cuando lo haga voy a tener que modificar gran parte del código que tengo guardado. Todo sea por hacer "internacionales" nuestras aplicaciones ....

Necesito este mismo pero en

Necesito este mismo pero en lenguaje c++
Les doy las gracias si pueden colaborarme.

Gracias a ambos por el

Gracias a ambos por el trabajo, solo un detallito:

En la funcion StrDecrypt. la linea

Src := TMemoryStream.Create;

genera un memory leak debido a que la funcion MIMEDeCode(Str, ADiscard) devuelve un TMemoryStream. asi que debemos eliminarla quedando asi:
function StrDecrypt(Str, Key: string): string;
var
  Src: TMemoryStream;
  Dst: TStringStream;
  Size: Integer;
  AESKey: TAESKey;
  ExpandedKey: TAESExpandedKey;
  ADiscard: Integer;
begin
  Result := EmptyStr;
  Dst := TStringStream.Create(Str);
  try
    KeyToAESKey(Key, AESKey);
    AESExpandKey(ExpandedKey, AESKey);{}
    Src := MIMEDeCode(Str, ADiscard);
    try
      Src.Position := 0;
      // Leemos el tamaño del texto
      Src.ReadBuffer(Size, Sizeof(Size));
      AESDecryptStreamECB(Src, Dst, ExpandedKey);
      Dst.Size := Size;
      Result := Dst.DataString;
    finally
      Src.Free;
    end;
  finally
    Dst.Free;
  end;
end;

Te felicito por el contenido

Te felicito por el contenido de tu pagina. Es lo mejorcito que he podido ver desde hace muucho tiempo

Excelente el código, sólo que

Excelente el código, sólo que al cifrar un archivo línea a línea, si las líneas son mayores de cierto largo quedan en dos línea, y además quedan con una línea adicional sólo con un espacio en blanco, por lo que para desencriptar el archivo, habrá que crear una rutina que maneje estos largos de línea... Pero son detalles, el Gran Trabajo lo realizó Usted...

MUCHAS GRACIAS!!!

El código tal como esta

El código tal como esta admite lineas de texto de hasta 2 GB, ¿a partir de que tamaño notas el problema?.

Muchas gracias por el aporte,

Muchas gracias por el aporte, pero tengo problemas con delphi 2010 sale un error en la linea
Src.ReadBuffer(Size, Sizeof(Size));

Agradezco se ayuda

¿Y cual es el error?

¿Y cual es el error?

Enviar un comentario nuevo

El contenido de este campo se mantiene privado y no se mostrará públicamente.