2009. április 14., kedd

Secure Compression for TStringList

Problem/Question/Abstract:

How to load and save encrypted string lists

Answer:

I combined routines from two open source projects to produce a simple way of saving a stringlist to disk in a compressed and very securely encrypted fashion (using the AES algorithm).

The two components are LockBox from Turbopower - available at
http://sourceforge.net/projects/tplockbox/ http://sourceforge.net/projects/tplockbox/
and UCL Compression Library API for Borland Delphi from
http://www.zeitungsjunge.de/delphi/ucl/index.htm http://www.zeitungsjunge.de/delphi/ucl/index.htm

The unit EncCompress is listed below. It defines a simple class TCompEnc which lets you load a text file into the stringlist. To save a plain text string list, first compress it using the UCLCompressStream, then feed this into the lockbox encryption component and save that to disk. Loading the encrypted file back in is just a matter of reversing the process, load the file, decrypt it then decompress it.

I've provided a simple test program - just drop a couple of buttons onto a form and hook up the click event to these routines. Make sure that the text for the key is the same in both!! You'll need a test  file 'test.txt', and after clicking both buttons you should find that the 'test2.txt' that is output is identical to the original.

I appreciate I've done two things which aren't very good- one use an internal member directly (enc.flist) and used a tMemoryStream to save and load files from disk instead of a TFileStream (before anyone points these out!)

Note: the AES (Rijndael) is symmetric so the same key encrypts and decrypts.

// Sample event handler code

procedure TForm1.Button1Click(Sender: TObject);
var
enc: TCompEnc;
list: tstringlist;
begin
Enc := TCompEnc.Create;
list := tstringlist.create;
list.LoadFromFile('test.txt');
enc.flist.Text := list.text;
enc.KeyText := 'hgjhgkjghkjh654328878';
enc.SaveToFile('test.dat');
enc.free;
list.free;
end;

procedure TForm1.Button2Click(Sender: TObject);
var
enc: TCompEnc;
begin
Enc := TCompEnc.Create;
Enc.KeyText := 'hgjhgkjghkjh654328878';
Enc.LoadFromfile('test.dat');
Enc.Flist.SaveToFile('test2.txt');
Enc.free;
end;

// The unit
unit EncCompress;

interface

uses
Windows, SysUtils, Classes, diuclstreams, LbCipher, LbClass;

type
TCompEnc = class
FList: TStringList;
FKeyText: string;
private
function GetList: TStringlist;
procedure SetKeyText(const Value: string);

public
constructor Create;
destructor Destroy; override;
procedure LoadFromFile(Filename: string);
procedure SaveToFile(Filename: string);
property List: TStringlist read GetList;
property KeyText: string write SetKeyText;
end;

implementation

uses math;

const
COMPRESSION_LEVEL = 3;
BUFFER_SIZE = $4000;

{ TCompEnc }

constructor TCompEnc.Create;
begin
inherited;
Flist := TStringList.Create;
end;

destructor TCompEnc.Destroy;
begin
FList.Free;
inherited;
end;

function TCompEnc.GetList: TStringlist;
begin
Result := Flist;
end;

// Step 1 Compress Flist Strings into stream
// Compress stream
// Save stream to file.

procedure TCompEnc.SaveToFile(Filename: string);
var
Str: TStringStream;
CompStream: TMemoryStream;
FileStream: TMemoryStream;
MyCipher: TLbRijndael;
begin
MyCipher := TLbRijnDael.Create(nil);
MyCipher.GenerateKey(FKeyText);

Str := tStringStream.Create(Flist.Text);
Str.Position := 0;
CompStream := TMemoryStream.create;
UclCompressStream(Str, CompStream, 3, BUFFER_SIZE);
Str.Free;
CompStream.Position := 0;
try
// Prepare key...
Filestream := tmemoryStream.Create;
MyCipher.EncryptStream(CompStream, FileStream);
FileStream.Position := 0;
FileStream.SaveToFile(Filename);
FileStream.Free;
finally
CompStream.Free;
end;
end;

// Loads File.
// 1st step, decrypt from AES to just Compressed
// 2nd step, Decompress into flist

procedure TCompEnc.LoadFromFile(Filename: string);
var
Source, Dest: TMemoryStream;
MyCipher: TLbRijnDael;
begin
// Decryption
Source := TMemoryStream.Create;
Source.LoadFromFile(Filename);
Source.Position := 0;
Dest := TMemoryStream.Create;
MyCipher := TLbRijnDael.Create(nil);
MyCipher.GenerateKey(FKeyText);

try
MyCipher.DecryptStream(Source, Dest);
MyCipher.Free;
Dest.Position := 0;
Source.Position := 0;

if UclDecompressStream(Dest, Source, BUFFER_SIZE) then
begin
Flist.Clear;
Source.Position := 0;
Flist.LoadFromStream(Source);
end;
finally
Dest.Free;
end;
Source.Free;
end;

procedure TCompEnc.SetKeyText(const Value: string);
begin
FKeyText := Value;
end;

end.


Nincsenek megjegyzések:

Megjegyzés küldése