Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > Delphi: Общие вопросы > DCPcrypt — криптография в Delphi.


Автор: kemiisto 9.7.2008, 11:44
Введение
http://ru.wikipedia.org/wiki/Криптография
Цитата
Криптогра́фия (от греч. κρυπτός — скрытый и γράφω — пишу) — наука о математических методах обеспечения конфиденциальности (невозможности прочтения информации посторонним) и аутентичности (целостности и подлинности авторства, а также невозможности отказа от авторства) информации.


Различают 3 группы криптографических алгоритмов:
http://ru.wikipedia.org/wiki/Алгоритм_с_симметричным_ключом;
http://ru.wikipedia.org/wiki/Криптосистема_с_открытым_ключом;
http://ru.wikipedia.org/wiki/Хэш-функция.

Алгоритмы первых двух групп служат для обеспечения  конфиденциальности информации, алгоритмы последней группы — для обеспечения  аутентичности.

Рано или поздно в жизни почти каждого программиста встаёт задача обеспечения конфиденциальности или аутентичности определённой информации. Не будучи специалистом в криптографии, большинство предпочитает использовать уже готовые алгоритмы. Но где же взять исходный код или компонент, обеспечивающий необходимую функциональность? Ответов может быть множество. В данной статье я расскажу об одной интересной библиотеке.

Итак, встречаем DCPcrypt Cryptographic Component Library v2 Beta 3. Взять библиотеку можно http://www.cityinthesky.co.uk/cryptography.html. Первое что бросается в глаза — открытость библиотеки (open source software). 

Установка
1. Заходим по адресу http://www.cityinthesky.co.uk/cryptography.html и качаем архив DCPcrypt v2 Beta 3. Прямая ссылка http://www.cityinthesky.co.uk/files/dcpcrypt2.zip.
2. Распаковываем содержимое архива в какую-нибудь папку. Я предпочитаю сторонние компоненты и библиотеки хранить в подпапках папки С:\Program Files\Borland\Delphi7\Lib\. Добавляем путь к созданной пакпе и её двум подпапкам Ciphers и Hashes в Library Path (в Delphi 7 в главном меню Tools — Enviroment Options, вкладка Library).
3. Открываем нужный пакет и устанавливаем (для Delphi 6-ой версии и выше выбираем DCPdelphi6.dpk).  

Использование 
После установки можно выбрать необходимый компонент с вкладки DCPciphers или DCPhashes, бросит его на форму и использовать. Но, так как компоненты невизуальные, я (как собственно и автор компонентов) предпочитаю использовать иной подход. Просто добавьте нужный модуль в раздел uses и используйте подпрограммы аналогичные тем, что я приведу ниже. В приведённых фрагментах в uses добавлены модули DCPrijndael и DCPsha1.

Для зашифрования строки используёте функцию наподобие той, что описана ниже:

Код

function EncryptString(Source, Password: string): string;
var
  DCP_rijndael1: TDCP_rijndael;
begin
  DCP_rijndael1 := TDCP_rijndael.Create(Self);   // создаём объект
  DCP_rijndael1.InitStr(Password, TDCP_sha1);    // инициализируем
  Result := DCP_rijndael1.EncryptString(Source); // шифруем
  DCP_rijndael1.Burn;                            // стираем инфо о ключе
  DCP_rijndael1.Free;                            // уничтожаем объект
end;


В принципе можно не вызывать метод Burn, т.к. он будет вызван автоматически при уничтожении объекта (Free), но даже сам разработчик считает не лишним явный вызов этого метода.

Для расшифрования строки используйте что-то наподобие:

Код

function DecryptString(Source, Password: string): string;
var
  DCP_rijndael1: TDCP_rijndael;
begin
  DCP_rijndael1 := TDCP_rijndael.Create(Self);   // создаём объект
  DCP_rijndael1.InitStr(Password, TDCP_sha1);    // инициализируем
  Result := DCP_rijndael1.DecryptString(Source); // дешифруем
  DCP_rijndael1.Burn;                            // стираем инфо о ключе
  DCP_rijndael1.Free;                            // уничтожаем объект
end;


Зашифрование/расшифрование файла:

Код

function EncryptFile(Source, Dest, Password: string): Boolean;
var
  DCP_rijndael1: TDCP_rijndael;
  SourceStream, DestStream: TFileStream;
begin
  Result := True;
  try
    SourceStream := TFileStream.Create(Source, fmOpenRead);
    DestStream := TFileStream.Create(Dest, fmCreate);
    DCP_rijndael1 := TDCP_rijndael.Create(Self);
    DCP_rijndael1.InitStr(Password, TDCP_sha1);
    DCP_rijndael1.EncryptStream(SourceStream, DestStream, SourceStream.Size);
    DCP_rijndael1.Burn;
    DCP_rijndael1.Free;
    DestStream.Free;
    SourceStream.Free;
  except
    Result := False;
  end;
end;


Код

function DecryptFile(Source, Dest, Password: string): Boolean;
var
  DCP_rijndael1: TDCP_rijndael;
  SourceStream, DestStream: TFileStream;
begin
  Result := True;
  try
    SourceStream := TFileStream.Create(Source, fmOpenRead);
    DestStream := TFileStream.Create(Dest, fmCreate);
    DCP_rijndael1 := TDCP_rijndael.Create(Self);
    DCP_rijndael1.InitStr(Password, TDCP_sha1);
    DCP_rijndael1.DecryptStream(SourceStream, DestStream, SourceStream.Size);
    DCP_rijndael1.Burn;
    DCP_rijndael1.Free;
    DestStream.Free;
    SourceStream.Free;
  except
    Result := False;
  end;
end;


Не забудем и про хеширование, основное применение которого — http://ru.wikipedia.org/wiki/Хэш-функция#.D0.A1.D0.B2.D0.B5.D1.80.D0.BA.D0.B0_.D0.B4.D0.B0.D0.BD.D0.BD.D1.8B.D1.85. 

Итак, для проверки на наличие ошибок (при передаче файла или во время хранения) нам необходимо уметь считать контрольную хэш-сумму файла:

Код

function GetFileHash(FileName: string): string;
var
  Hash: TDCP_sha1;
  Digest: array[0..19] of byte; //sha1 вычисляет 160-битовую хэш-сумму (20 байт)
  Source: TFileStream;
  i: integer;
begin
  Source:= TfileStream.Create(FileName,fmOpenRead);
  Hash:= TDCP_sha1.Create(Self);               // создаём объект
  Hash.Init;                                   // инициализируем
  Hash.UpdateStream(Source,Source.Size);       // вычисляем хэш-сумму
  Hash.Final(Digest);                          // сохраняем её в массив
  Source.Free;                                 // уничтожаем объект
  Result := DigestToStr(Digest);               // получаем хэш-сумму строкой
end;


Как Вам уже должно быть http://ru.wikipedia.org/wiki/Хэш-функция#.D0.A1.D0.B2.D0.B5.D1.80.D0.BA.D0.B0_.D0.B4.D0.B0.D0.BD.D0.BD.D1.8B.D1.85, в большинстве случаев парольные фразы не хранятся на целевых объектах, хранятся лишь их хеш-значения. В ходе процедуры аутентификации вычисляется хеш-значение введённой парольной фразы, и сравнивается с сохранённым. Так что подсчёт хэш-суммы строки тоже может пригодиться:

Код

function GetStringHash(Source: string): string;
var
  Hash: TDCP_sha1;
  Digest: array[0..19] of Byte;
begin
  Hash := TDCP_sha1.Create(Self); // создаём объект
  Hash.Init;                      // инициализируем
  Hash.UpdateStr(Source);         // вычисляем хэш-сумму
  Hash.Final(Digest);             // сохраняем её в массив
  Hash.Free;                      // уничтожаем объект
  Result := DigestToStr(Digest);  // получаем хэш-сумму строкой
end;


Думаю, Вы заметили небольшой нюанс: вычисляемая хэш-сумма представляет собой массив байтов. Для приведения к «нормальному» строковому виду я использую небольшую дополнительную функцию: 

Код

function DigestToStr(Digest: array of byte): string;
var
  i: Integer;
begin
  Result := '';
  for i := 0 to 19 do
    Result := Result + LowerCase(IntToHex(Digest[i], 2));
end;


Ну и наконец, скажу пару слов об основном преимуществе описываемой библиотеки. Все алгоритмы шифрования — наследники TDCP_cipher, а хэширования — наследники  TDCP_hash. Поэтому, если по какой-либо причине Вам захочется изменить алгоритм, это делается минимальными изменениями кода. Например, в нашей первой функции:

Код

uses 
  DCPrijndael, ...

...

function EncryptString(Source, Password: string): string;
var
  DCP_rijndael1: TDCP_rijndael;
begin
  DCP_rijndael1 := TDCP_rijndael.Create(Self);   // создаём объект
  DCP_rijndael1.InitStr(Password, TDCP_sha1);    // инициализируем
  Result := DCP_rijndael1.EncryptString(Source); // шифруем
  DCP_rijndael1.Burn;                            // стираем инфо о ключе
  DCP_rijndael1.Free;                            // уничтожаем объект
end;


изменим алгоритм шифрования с AES(Rijndael) на 3DES:

Код

uses
  DCPdes, ...

...

function EncryptString(Source, Password: string): string;
var
  DCP_3des1: TDCP_3des;
begin
  DCP_3des1 := TDCP_3des.Create(Self);       // создаём объект
  DCP_3des1.InitStr(Password, TDCP_sha1);    // инициализируем
  Result := DCP_3des1.EncryptString(Source); // шифруем
  DCP_3des1.Burn;                            // стираем инфо о ключе
  DCP_3des1.Free;                            // уничтожаем объект
end;


Надеюсь, статья окажется полезной. Удачи! smile 

P.S. В аттаче статья в PDF.

Автор: Alexandr87 9.7.2008, 13:39
Цитата

Различают 3 группы криптографических алгоритмов:
симметричные;
ассиметричные;
хэш-функций.

Алгоритмы первых двух групп служат для обеспечения  конфиденциальности информации, алгоритмы последней группы — для обеспечения  аутентичности.


Это откуда? и в какую из этих групп отнести MACи? В какую ЭЦП?

Автор: kemiisto 9.7.2008, 13:58
Цитата(Alexandr87 @  9.7.2008,  14:39 Найти цитируемый пост)
Это откуда?

http://ru.wikipedia.org/wiki/Криптография#.D0.A1.D0.BE.D0.B2.D1.80.D0.B5.D0.BC.D0.B5.D0.BD.D0.BD.D0.B0.D1.8F_.D0.BA.D1.80.D0.B8.D0.BF.D1.82.D0.BE.D0.B3.D1.80.D0.B0.D1.84.D0.B8.D1.8F
Цитата

Распространенные алгоритмы:

  • симметричные DES, AES, ГОСТ 28147-89, Camellia, Twofish, Blowfish, IDEA, RC4 и др.
  • ассиметричные RSA и Elgamal (Эль-Гамаль)
  • хэш-функций MD4, MD5, SHA-1, ГОСТ Р 34.11-94



Цитата(Alexandr87 @  9.7.2008,  14:39 Найти цитируемый пост)
 и в какую из этих групп отнести MACи? В какую ЭЦП?

Если Вы не заметили, статья называется не "Введение в криптографию". Она посвящена использованию конкретной библиотеки. Я в начале дал ссылочку на народную энциклопедию. В этой статье есть информация и про электронную цифровую подпись. Зачем же сюда копи-пастить? 

Автор: December 9.7.2008, 14:13
Как раз вчера работал с этой либой... Так что возьму на себя смелость немного расширить статью smile

В качестве ключа я использовал не хэш от строки, а свой готовый 256-байтный ключ, записанный в строку. Кроме того, у меня могут использоваться два ключа - для демо и про версий. В таком раскладе легко ошибиться при передаче ключа методу Init - простое копирование ключа в локальную строковую переменную не катит. 
Код

uses
  ..., DCPrc4;

const
  KeySize = 256;//bytes

var
  KeyDemo:string = 'dj4lgmnitG4gggfdASEg45g4g3j7rK620{N2YZYiX4 DT]}'+
    '5rf7S/kMNm,n./OK/ilUN..u8h.H6fJFCj5DFNY6GMvbm,6FmTFMy6FMtfMy5fdN564BtErb'+
    '7gl7G,ubKTRF645djDRJ5Dmy76l8h;9J;9J;8hfUFCGcgD4f4SHxnYhb,nvnvchgFH5RUrfO'+
    'dskjhfpqo9s97hgBL7BL7bl7ghl7hGL87GLGl7l7glxdrgfcg58KHJghdfdrpPnbU';

  KeyRelease:string = 'DJFDKSFghjyg;KH9bn6CRTXCx4hUGLB.8.nkVTJ6FJfjylk7gl7GLUHm'+
    'HG7gnkBk8jhKkKJHK87HkjkFGF6PCbV9KaK81WWYgP[CR[yjILWv2_SBE]AsLEz_8sBZ3LV5N'+
    'Go0NLL1om4 XbALjhgkk7sda823r23;d923NrU}dkzPp5 DkJ2_8JvYmWFn LR3CRxyfswsto'+
    'cvnkscv78h2lk8HHKhlkjdfvsd;vlkvsd0vvds;ldvhyB[NXzl5y5Z';

 

function EncryptString(source:string):string;
var
  encrypted:string
  len:integer;
  Cipher: TDCP_rc4;
  Key:pointer;
begin
case Form1.RadioGroup1.ItemIndex of
  0:Key:=@KeyDemo[1];//Demo
  1:Key:=@KeyRelease[1];//Pro
end;//case

len:=length(source);
SetLength(encrypted,len);
Cipher:=TDCP_rc4.Create(Self);
Cipher.Init(Key^,KeySize*8,nil);
Cipher.Encrypt(source[1],encrypted[1],len);
Cipher.Burn;
Cipher.Free;

result:=encrypted;
end;


Как уже упоминалось, разумнее не засорять IDE компонентами, а напрямую подключить модули к проге. В uses требуется только один файл, и он тянет за собой ещё всего три:
DCPbase64
DCPconst
DCPcrypt2

Автор: Alexandr87 10.7.2008, 07:22
Цитата(kemiisto @  9.7.2008,  16:58 Найти цитируемый пост)
Если Вы не заметили, статья называется не "Введение в криптографию". Она посвящена использованию конкретной библиотеки. Я в начале дал ссылочку на народную энциклопедию. В этой статье есть информация и про электронную цифровую подпись. Зачем же сюда копи-пастить? 

Зачем тогда вообще в статье это введение, тем более информация в нем не совсем соотвествует действительности. В той же вики, такого не написано - вчитайтесь внимательно.

Цитата(kemiisto @  9.7.2008,  14:44 Найти цитируемый пост)
Для дешифрования строки используйте что-то наподобие:

Вы, вероятно, хотели сказать расшифрования.

Я не настаиваю на исправлении. Не хотите - не исправляйте. Тут дело каждого - писать грамотно, или нет.

Автор: Poseidon 10.7.2008, 08:40
Цитата(Alexandr87 @  10.7.2008,  07:22 Найти цитируемый пост)
Тут дело каждого - писать грамотно, или нет
"Дешифрование" - это правильно написано. Появилось это слово от decrypt и значит то же самое, что и "расшифрование". Alexandr87, не придирайтесь.

Автор: Alexandr87 10.7.2008, 11:57
Poseidon, для вас может быть и нет разницы, а вообще расшифрование и дешифрование это два совершенно разных процесса. 
http://ru.wikipedia.org/wiki/%D0%9A%D1%80%D0%B8%D0%BF%D1%82%D0%BE%D0%B3%D1%80%D0%B0%D1%84%D0%B8%D1%8F#.D0.A2.D0.B5.D1.80.D0.BC.D0.B8.D0.BD.D0.BE.D0.BB.D0.BE.D0.B3.D0.B8.D1.8F
Я не придираюсь - я поправляю.

Автор: kemiisto 10.7.2008, 12:25
Alexandr87, ну это всё тонкости перевода. Моему слуху приятнее шифрование/дешифрование, а не зашифрование/расшифрование. Вы же прекрасно понимаете, что имеется в виду. А значит, придираетесь. smile 

Автор: Alexandr87 10.7.2008, 12:51
Ваша компетентность в данном вопросе мне понятна.

Автор: kemiisto 11.7.2008, 13:33
Alexandr87, приношу Вам свои извинения. 

Вы действительно правы. Терминология вещь сугубо важная. Проблема в том, что я ознакомился с ней по электронной книге Брюс Шнайер - Прикладная криптография, 2-е изд. Как оказалось, это так сказать "левый" перевод. Вот ссылка о качестве этого перевода: http://www.ssl.stu.neva.ru/psw/crypto/appl_rus/appl_cryp.htm.

Ещё раз приношу Вам извинения. 

Автор: vzf 4.9.2008, 19:42
Абсолютно согласен с критикой Alexandr87.

Вот это :

Цитата

Различают 3 группы криптографических алгоритмов:
симметричные;
ассиметричные;
хэш-функций.

Алгоритмы первых двух групп служат для обеспечения  конфиденциальности информации, алгоритмы последней группы — для обеспечения  аутентичности.



не верно и даже в приведенной статье в википедии об этом не говорится.

А книжку под редакцией Семьянова думаю стоит почитать, если интересует вопрос, думаю хорошая книжка, т.к. сам он специалист в своем деле smile Вот  http://www.ssl.stu.neva.ru/psw/crypto/c_cpp_rus/c_cpp.htm книжка также ничего, она тоже под его редакцией.

Автор: kemiisto 23.9.2008, 14:58
vzf, я не против критики. Но критики конструктивной. Если Вы специалист - подскажите как написать правильно и я исправлю. А то:
Цитата(vzf @  4.9.2008,  20:42 Найти цитируемый пост)
не верно

И что дальше? Исправить на:
Цитата

Распространенные алгоритмы:
    * симметричные DES, AES, ГОСТ 28147-89, Camellia, Twofish, Blowfish, IDEA, RC4 и др.
    * ассиметричные RSA и Elgamal (Эль-Гамаль)
    * хэш-функций MD4, MD5, SHA-1, ГОСТ Р 34.11-94

Так, видимо, правильно?

Цитата(vzf @  4.9.2008,  20:42 Найти цитируемый пост)
А книжку под редакцией Семьянова думаю стоит почитать

Время... Где б его взять...

Автор: vppm 25.9.2009, 12:32
Alexandr87
от вашей неуместной критики пользы читателю - ноль, а автору - спасибо!

Автор: kami 25.9.2009, 20:14
Пользуюсь этой библиотекой не первый год. Лучшее, что приходилось использовать в плане удобоваримости, настраиваемости и т.п. По моим тестам самый быстрый из всех cipher-ов это TDCP_blowfish.
Имхо, в статье нехватает нескольких фраз про crypt|decrypt:
расшифровка должна идти в том же порядке, что и шифрование. то есть, если сделать
Код

Encrypt(str1...);
encrypt(str2...);

то в том же порядке нужно вызывать и decrypt. Если поменять порядок - результат будет совсем не тот, который ожидается.
Если данные могут расшифровываться в другом порядке (как пример - если зашифрованные данные передаются по UDP), то после каждого Encrypt (и естественно - после decrypt) нужно выполнять Reset. Правда, толку тогда от стойкого алгоритма поубавится.

Автор: ikrom 1.10.2009, 11:53
Добрый день!

Помогите, пожалуйста, решить одну проблемку:

Есть БД Mysql- таблица 'пользователи' в котором через PHP (web приложение) захэшированы пароли пользователей, теперь через Delphi (Window приложение) нужно путем запроса проверять сходства между вводимым пользователем пароль и захэшированного пароля в таблице. Как захэшировать в sha1 вводимый пользователем в Edit текст, в Delphi?


Заранее благодарен! 

Автор: loveisafix 16.11.2009, 00:33
Здравствуйте, уважаемые!

имеется хеш вида EE5B0E48C8FE97716D. Исходная строка - "1234". Известно что фиксированный ключ был такого вида: 23 82 107 6 35 78 88 7

при помощи примера в статье пытаюсь сгенерировать такой же хеш.

Код

DCP_3des1 := TDCP_3des.Create(Self);       // создаём объект
DCP_3des1.InitStr(PCHAR('238210763578887'), TDCP_sha1);    // инициализируем
Edit2.Text := DCP_3des1.EncryptString(Edit1.Text); // шифруем


Ничего не выходит. Только короткая строка "Eck4fQ=="... Как быть..
Если кто-то может помочь - буду рад! Заранее спасибо!

С уважением Дмитрий. loveisafix ЭТ gmail.com

Автор: Georg97 15.5.2013, 15:06
Ошибка UI в переменнjой Self
what's up?

Автор: ReGeDiT 29.12.2013, 04:43
Цитата(Georg97 @ 15.5.2013,  16:06)
Ошибка UI в переменнjой Self
what's up?

такая-же возня )
наверное, так-же используете версию Делфи около 2009-2010...


глупо, конечно, но лечится это довольно просто:
Код

..
  DCP_rijndael1 := TDCP_rijndael.Create(DCP_rijndael1);
..


или объявить Self:
Код

...
Self : TComponent;
...
Self := DCP_rijndael;
...


хрен его знает, будет ли работать = Delphi сыпит warnings

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)