воскресенье, 8 февраля 2009 г.

Шифрование с закрытым ключём

Рассмотрим пример классического шифрования с закрытым ключём.
Данное шифрование считается ненадёжным, если пароль заложен в программу. Если пароль вводится пользователем, то стойкость зависит от сложности пароля.
Данный пример создан для удобства хранения шифрованных данных в реестре или текстовых и ini файлах. То есть, шифрованные бинарные данные возвращаются в текстовом HEX виде.
В представленом ниже коде всего две функции:

function EncString(s: string; pass: string = def; alg: Cardinal = CALG_RC4): string;
function DecString(s: string; pass: string = def; alg: Cardinal = CALG_RC4): string;
Первая шифрует, вторая, соответственно, дешифрует.
Первый параметр обязательный - это сами данные.
Второй параметр необязательный - это пароль. Если не указан, используется внутренний фиксированный пароль (константа def).
Третий параметр совсем необязательный - алгоритм шифрования. Всего два варианта - RC2 и RC4. Для шифрования паролей лучше использовать RC2, т.к. в RC4 видно, сколько байт зашифровано. Для всего остального можно использовать RC4 (по умолчанию), чтоб не добавлять лишнего мусора.
Пример использования:
procedure TForm1.Button1Click(Sender: TObject);
begin
Edit2.Text := EncString(Edit1.Text);
Edit1.Text := '';
end;

procedure TForm1.Button2Click(Sender: TObject);
begin
Edit1.Text := DecString(Edit2.Text);
end;
Далее сам код модуля.
Код не использует других модулей (даже Windows). Построен на CryptoAPI функциях системы, поэтому практически не добавляет веса программе и может использоваться, как в VCL проектах, так и в проектах на чистом API.
Шифрованные данные "подсаливаются" так, что одни и те же данные, зашифрованные дважды, дают разный результат, что практически исключает возможность подобрать ключ анализом, а не тупым брутфорсом.
unit MiniCry;

interface

const
CALG_RC4 = ((3 shl 13) or (4 shl 9) or 1);
CALG_RC2 = ((3 shl 13) or (3 shl 9) or 2);
def = 'WrSxnCNBpJ7Ko4[e",7Ty)a0ykP)62Ce[.bAA;SuOf4*{nagx4s,;5!eHU!v=p3z';

function EncString(s: string; pass: string = def; alg: Cardinal = CALG_RC4): string;
{* зашифровать строку }
function DecString(s: string; pass: string = def; alg: Cardinal = CALG_RC4): string;
{* расшифровать строку }

implementation

const
ADVAPI32 = 'advapi32.dll';
PROV_RSA_FULL = 1;
CRYPT_VERIFYCONTEXT = $F0000000;
CALG_SHA = ((4 shl 13) or 0 or 4);

type
HCRYPTPROV = Cardinal;
HCRYPTKEY = Cardinal;
ALG_ID = Cardinal;
PHCRYPTPROV = ^HCRYPTPROV;
PHCRYPTKEY = ^HCRYPTKEY;
LPAWSTR = PWideChar;
HCRYPTHASH = Cardinal;
PHCRYPTHASH = ^HCRYPTHASH;

function CryptReleaseContext(hProv:HCRYPTPROV;dwFlags:LongWord):LongBool;stdcall;external ADVAPI32 name 'CryptReleaseContext';
function CryptAcquireContext(Prov:PHCRYPTPROV;Container:LPAWSTR;Provider:LPAWSTR;ProvType:LongWord;Flags:LongWord):LongBool;stdcall;external ADVAPI32 name 'CryptAcquireContextW';
function CryptEncrypt(Key:HCRYPTKEY;Hash:HCRYPTHASH;Final:LongBool;Flags:LongWord;Data:PBYTE;Len:PLongWord;BufLen:LongWord):LongBool;stdcall;external ADVAPI32 name 'CryptEncrypt';
function CryptDecrypt(Key:HCRYPTKEY;Hash:HCRYPTHASH;Final:LongBool;Flags:LongWord;Data:PBYTE;Len:PLongWord):LongBool;stdcall;external ADVAPI32 name 'CryptDecrypt';
function CryptCreateHash(Prov:HCRYPTPROV;Algid:ALG_ID;Key:HCRYPTKEY;Flags:LongInt;Hash:PHCRYPTHASH):LongBool;stdcall;external ADVAPI32 name 'CryptCreateHash';
function CryptHashData(Hash:HCRYPTHASH;Data:PBYTE;DataLen :LongInt;Flags:LongInt):LongBool;stdcall;external ADVAPI32 name 'CryptHashData';
function CryptDeriveKey(Prov:HCRYPTPROV;Algid:ALG_ID;BaseData:HCRYPTHASH;Flags:LongInt;Key:PHCRYPTKEY) :LongBool;stdcall;external ADVAPI32 name 'CryptDeriveKey';
function CryptDestroyHash(hHash :HCRYPTHASH) :LongBool;stdcall;external ADVAPI32 name 'CryptDestroyHash';

function ByteToHex(b: byte): string;
function GetChar(b: byte): char;
begin
if b < 10 then Result := chr(Ord('0') + b)
else Result := chr(Ord('A') - 10 + b);
end;
begin
Result := GetChar(b div 16) + GetChar(b mod 16);
end;

function StringToHex(const s: string): string;
var
i: integer;
begin
result := '';
for i := 1 to Length(s) do
result := result + ByteToHex(ord(s[i]));
end;

function StrToIntDef(s: string; def: integer): integer;
var
i, c: integer;
begin
Val(s, i, c);
if c = 0 then Result := i else Result := def;
end;

function HexToString(const s: string): string;
var
i: integer;
begin
result := '';
for i := 1 to Length(s) div 2 do
try result := result + chr(StrToIntDef('$' + copy(s, i*2-1, 2), 32));
except result := result + '?'; end;
end;

procedure InitPass(pass: string; alg: LongWord; var hProv: HCRYPTPROV; var hSKey: HCRYPTKEY);
var
hash: HCRYPTHASH;
begin
CryptAcquireContext(@hProv, nil, nil, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT);
CryptCreateHash(hProv, CALG_SHA, 0, 0, @hash);
CryptHashData(hash, @pass[1], length(pass), 0);
CryptDeriveKey(hProv, alg, hash, 0, @hSKey);
CryptDestroyHash(hash);
end;

function EncString(s: string; pass: string = def; alg: Cardinal = CALG_RC4): string;
var
p: PByte;
sz: LongWord;
hProv: HCRYPTPROV;
hSKey: HCRYPTKEY;
begin
InitPass(pass, alg, hProv, hSKey);
Insert(chr(Random(256)), s, 1);
sz := Length(s);
GetMem(p, sz + 8); move(s[1], p^, sz);
if CryptEncrypt(hSKey, 0, true, 0, p, @sz, sz + 8) then
begin
SetLength(result, sz);
move(p^, result[1], sz);
result := StringToHex(result);
end else result := s;
FreeMem(p);
CryptReleaseContext(hProv, 0);
end;

function DecString(s: string; pass: string = def; alg: Cardinal = CALG_RC4): string;
var
p: PByte;
sz: LongWord;
hProv: HCRYPTPROV;
hSKey: HCRYPTKEY;
begin
InitPass(pass, alg, hProv, hSKey);
s := HexToString(s);
sz := Length(s);
GetMem(p, sz); move(s[1], p^, sz);
if CryptDecrypt(hSKey, 0, true, 0, p, @sz) then
begin
SetLength(result, sz);
move(p^, result[1], sz);
delete(result, 1, 1);
end else result := s;
FreeMem(p);
CryptReleaseContext(hProv, 0);
end;

initialization
Randomize;

end.

3 комментария: