уважаемые посетители блога, если Вам понравилась, то, пожалуйста, помогите автору с лечением. Подробности тут.

В одном из постов про OAuth и Twitter я говорил, что с имеющейся в наличии и единственной найденной мной библиотеке Delphi для работы с OAuth есть одна проблема — проблема русских символов. То есть текст, состоящий только из ANSI-символов передается в Твитер нормально, а стоит добавить хоть один не ANSI символ и происходит сбой — сервер не распознает подпись.
Перерыв кучу информации на эту тему вроде бы нашел причину проблемы — ошибка в шифровании подписи. И вот, в поисках ошибки и родился этот незатейливый пост в котором я приведу примеры того как можно шифровать строки средствами библиотек Indy и Synapse. Так сказать, небольшое руководство для новичков по работе с такими алгоритмами как MD5, Base64 и т.д.

Следуя простой логике можно сказать, что независимо от того какую библиотеку, функции, операции и т.д. мы используем для реализации алгоритма шифрования — результат должен быть одинаков. Иначе теряется всякий смысл алгоритмов. Поэтому я и решил первым дело посмотреть одинаков ли будет результат работы с одними и теме же алгоритмами шифрования в Synapse и Indy. При работе с OAuth используются следующие алгоритмы:
1. MD5
2. Base64
3. HMAC-SHA-1
При этом иногда требуется, например, найти хэш строки в MD5 и кодировать её с помощью Base64. Начнем, пожалуй с самого простого алгоритма — MD5.

Алгоритм MD5

Досье из Википедии:

MD5 (англ. Message Digest 5) — 128-битный алгоритм хеширования, разработанный профессором Рональдом Л. Ривестом из Массачусетского технологического института в 1991 году. Предназначен для создания «отпечатков» или «дайджестов» сообщений произвольной длины. Является улучшенной в плане безопасности версией MD4. Зная MD5-образ (называемый также MD5-хеш или MD5-дайджест), невозможно восстановить входное сообщение, так как одному MD5-образу могут соответствовать разные сообщения. Используется для проверки подлинности опубликованных сообщений путём сравнения дайджеста сообщения с опубликованным. Эту операцию называют «проверка хеша» (hashcheck).

Алгоритм MD5 используется в OAuth при генерации параметра oauth_nonce. При этом полученный хэш передается серверу в HEX-формате. Реализуем алгоритм MD5 на Indy.

Алгоритм MD5 в Indy

Для работы нам понадобится модуль IdHashMessageDigest.pas. Подключаем его в uses. Для работы с MD5 в Indy предусмотрен отдельный класс TIdHashMessageDigest5, который является наследником от TIdHashMessageDigest4 (этот класс в свою очередь реализует алгоритм MD4 и является наследником от TIdHashMessageDigest и т.д. до TObject). Для того, чтобы получить MD5-хэш нам потребуется выполнить следующие операции:

....
var md5indy: TIdHashMessageDigest;
     hash, HEXhash, Base: string;
begin
  Base:='Hello World';
  md5indy:=TIdHashMessageDigest5.Create;//создаем экземпляр объекта
  hash:=StringOf(md5indy.HashString(Base));//получаем MD5-хэш
  HEXhash:=md5indy.HashStringAsHex(Base);//тот же хэш, но в HEX-форме
end;

Здесь метод HashString принимает на входе строку, вычисляет хэш и возвращает его в виде массива байтов (TidBytes). Поэтому дополнительно мы преобразуем этот массив в строку, используя функцию StringOf.
Метод HashStringAsHex также вычисляет MD5-хэш, но в дополнение сразу же его переводит в HEX-форму. Что тут скачать? Всё просто и удобно. Посмотрим как обстоит дело в Synapse.

Алгоритм MD5 в Synapse

Для работы с алгоритмами шифрования в Synapse предусмотрен отдельный модуль — synacode.pas в котором сосредоточены всевозможные методы реализующие самые распространенные методы шифрования. Подключаем модуль и находим в нем функцию для работы с MD5. Она так и называется:

function MD5(const Value: AnsiString): AnsiString;

Что сразу бросается в глаза после использования Indy? То, что функция принимает не массив байтов, а строку в ANSI. Это обстоятельство можно рассматривать и как плюс и как недостаток. Плюс, на мой взгляд состоит в том, что не требуется предварительно переводить строку в массив байтов. Недосаток же состоит в том, что если мы попробуем «скормить» функции следующую строку:
var S:string;
begin
result:=MD5(S)
end;
То на выходе окажется неверный результат. Поэтому предварительно нам необходимо позаботиться о том, чтобы строка была именно в ANSI-кодировке. О том как конвертировать строки я уже говорил. В модуле synacode отсутствует функция, возвращающая MD5-хэш в HEX-форме, поэтому придётся немного поработать самим (либо воспользоваться модулем synautil.pas в котором есть функция StrToHex).
Итак, получение MD5-хэшей в Synapse можно представить следующим образом:

var hex,md5hash, Base:string;
     Bytes: TBytes;
     ch: byte;
begin
  Base:='Hello World';
  md5hash:=MD5(Utf8ToAnsi(Hello World));
  hex:='';
  Bytes:=BytesOf(md5hash);
  for ch in Bytes do
     hex:=hex+IntToHex(ch,2);
end;

Вначале мы конвертируем строку Base в кодировку Ansi и вычисляем MD5-хэш. Затем я не стал подключать лишний модуль в uses и просто перевел хэш в массив байтов и для каждый элемент массива перевел в HEX, получив на выходе из цикла ровно такую же строку, как и при использовании функции в Indy HashStringAsHex.

Алгоритм Base64

Досье из Википедии:

Base64 буквально означает — позиционная система счисления с основанием 64. Здесь 64 — это наибольшая степень двойки (26), которая может быть представлена с использованием печатных символов ASCII. Эта система широко используется в электронной почте для представления бинарных файлов в тексте письма (транспортное кодирование). Все широко известные варианты, известные под названием Base64, используют символы A-Z, a-z и 0-9, что составляет 62 знака, для остальных двух знаков в разных системах используются различные символы.

Опять же в OAuth алгоритм base64 используется перед отправкой сообщения на сервер.

Алгоритм Base64 в Indy

Для реализации Base64 в Delphi средствами Indy нам опять же требуется подключать ещё один модуль в uses — IdCoderMIME.pas и использовать класс TIdEncoderMIME — наследник от TIdEncoder, который на сей раз имеет в своем составе классовый метод:

class function EncodeBytes(const ABytes: TIdBytes): string;

Таким образом, для кодирования строки в Base64 нам необходимо:
1. Перевести строку в массив байтов;
2. Кодировать массив в Base64
Реализуется это следующим образом:

var Base:string;
     base64String: string;
begin
  Base:='Hello World';
  base64String:=TIdEncoderMIME.EncodeBytes(ToBytes(Base));
end;

На выходе получим строку представленную в форме Base64.

Алгоритм Base64 в Synapse

Модуль SynaCode.pas содержит следующие функции для работы с Base64:

function EncodeBase64(const Value: AnsiString): AnsiString;
function DecodeBase64(const Value: AnsiString): AnsiString;
function EncodeBase64mod(const Value: AnsiString): AnsiString;
function DecodeBase64mod(const Value: AnsiString): AnsiString;

Последние две функции реализуют, как понятно из названия, модифицированный алгоритм кодирования Base64, который используется, например, при работе с IMAP.
Порядок кодирования строки в Base64 с использованием Synapse тот же, что и в MD5:
1. Переводим строку в кодировку ANSI
2. Передаем строку в функцию EncodeBase64.
Листинг:

var Base, Base64String: string;
begin
  Base:='Hello World';
  Base64String:=EncodeBase64(Utf8ToAnsi(Base));
end;

Алгоритм HMAC

Досье из Википедии:

HMAC (сокращение от англ. hash-based message authentication code, хеш-код идентификации сообщений) — алгоритм усиления криптостойкости других криптоалгоритмов (чаще всего MD5). Авторы — Хьюго Кравчик, Михир Беллар и Ран Каннетти.

Применительно к OAuth в Твитере HMAC используется для усиления алгоритма SHA-1:

Secure Hash Algorithm 1 — алгоритм криптографического хеширования. Описан в RFC 3174. Для входного сообщения произвольной длины (максимум 264 ? 1 бит) алгоритм генерирует 160-битное хеш-значение, называемое также дайджестом сообщения. Используется во многих криптографических приложениях и протоколах. Также рекомендован в качестве основного для государственных учреждений в США. Принципы, положенные в основу SHA-1, аналогичны тем, которые использовались Рональдом Ривестом при проектировании MD4.

Таким образом нам необходим алгоритм, реализующий вначале хэширование строки с использованием SHA-1, а затем усилить всё это дело алгоритмом HMAC. Благо, что в Indy, что в Synapse уже имеются для этого готовые решения.

HMAC-SHA1 в Indy

Опять же, следуя доброй традиции Indy, реализация HMAC-SHA1 в Delphi содержится в отдельном модуле — IdHMACSHA1.dcu. Подключаем модуль и видим, что для работы нам понадобится класс TIdHMACSHA1. Также для работы с алгоритмом нам понадобится некий ключ (key) — строка по которой и будет проходить шифрование. Реализовать алгоритм HMAC-SHA1 в Indy можно, например так (в виде отдельной функции):

function EncryptHMACSha1(Input, AKey: string): TIdBytes;
begin
   with TIdHMACSHA1.Create do
   try
     Key := ToBytes(AKey);
     Result := HashValue(ToBytes(Input));
   finally
     Free;
   end;
  end;

На выходе из функции получим массив байтов, который простой операцией:

StringOf()

можно преобразовать в строку.

HMAC-SHA1 в Synapse

В Synapse для работы с HMAC предусмотрены следующие функции:

function HMAC_MD5(Text, Key: AnsiString): AnsiString;
function HMAC_SHA1(Text, Key: AnsiString): AnsiString;

Соответственно для нас сейчас необходим второй метод.

var Base, HMACString: string;
begin
  Base:='Hello World';
  HMACString:=HMAC_SHA1(Utf8ToAnsi(Base));
end;

Как видите, что в Indy, что в Synapse использовать алгоритмы шифрования достаточно просто. Не подумайте, что я ярый противник Indy, но по-моему, несмотря на то, что Synapse достаточно старая библиотека и давно не обновлялась, работать с Synapse проще. Чтобы понять как использовать Base64 в Indy мне пришлось поднимать мануалы по этой библиотеке, смотреть исходники и т.д. Ушло время. В Synapse — достаточно было открыть всего один модуль. А результат работы одинаков. Сейчас пробую переписать библиотеку OAuth под использование Synapse и дополнительно просматриваю все части кода на наличие злосчастного глюка в кодировании. Надеюсь, что задача окажется мне по силам :)

0 0 голоса
Рейтинг статьи
уважаемые посетители блога, если Вам понравилась, то, пожалуйста, помогите автору с лечением. Подробности тут.
Подписаться
Уведомить о
18 Комментарий
Межтекстовые Отзывы
Посмотреть все комментарии
zibman
zibman
05/02/2011 03:17

Огромное спасибо. А то даже на сайте Embarcadero поиск SHA1 и MD5 ничего не дает.

Night4x
Night4x
15/05/2011 00:06

Очень полезная статья. Самое главное что реализация MD5 описана на Indy.
К стати я несовсем понял почему мы объявляем переменную типа «TIdHashMessageDigest», а не «TIdHashMessageDigest5». Я написал вот такой код и он работал:

[code]
var
res:string;
hash:TIdHashMessageDigest5;
begin
hash:=TIdHashMessageDigest5.Create;
res:=hash.AsHex(hash.HashValue(str));
end;  
[/code]

ORION
ORION
24/01/2016 18:48
Ответить на  Night4x

Thanks!

vahan
vahan
08/06/2011 11:55

Спасибо отличная статься но есть один вопрос можно ли в делфи реализовать аналог такого из .Net
SHA1 sha = new SHA1CryptoServiceProvider();
byte[] Hash1 = sha.ComputeHash(StrToByteArray(richTextBox1.Text));
 
Проблема в том что я не могу понять какой ключ используется в  .Net
В коде как видно ключа нет

Witcher
Witcher
27/02/2012 12:36

Спасибо за отличную статью!
Было бы интересно, если бы еще появился материальчик относительно обратимого шифрования. Иногда и такое нужно.
Кстати, заметил маленькую опечатку в теге статьи — стоит тег Synase а должен быть Synapse.

Марат
Марат
18/06/2012 10:55

В статье нет информации, по как мне кажется очень важному моменту. При кодировании строки в Base64 нужно учитывать, что Base64 кодирует бинарные данные в строки, то есть прежде чем кодировать строку, строка кодируется в бинарный вид через UTF-8, Ansi, Unicode и так далее. Если я правильно понимаю функция Indy ToBytes(Base) использует по умолчанию Ansi кодировку, а в примере с Synapse используется Utf-8 кодировка.

Артем
Артем
19/04/2013 15:17

А не пробовали делать хеш HMAC-SHA512 в Indy& Любой другой HMAC-SHA, кроме HMAC-SHA1, при вызове HashValue вылетает в AV.

Артем
Артем
24/04/2013 11:32
Ответить на  Vlad

С AV при вызове других хешей разобрался. Но как назло проблемы с кодировкой строки хэша возникли.
Допустим HMAC-SHA1.
Если использовать StringOf для преобразования TIdBytes то получается следующие кракозябры: чнўуји•%zЅ4жвIЮ.
Преобразования в другие кодировки не помогают. Должно быть f7eda2f3bc0fe81c95257abd34e61f1ce21549de. Не подскажете где собака порылась?

Артем
Артем
25/04/2013 13:23
Ответить на  Артем

Все разобрался.

Kayfolom
Kayfolom
26/11/2013 16:45
Ответить на  Артем

Артем, поздравляю с тем что разобрались, но нельзя ли поподробнее? ;) Как именно победили кодировку?

Akella225
Akella225
09/02/2015 17:12

А InDy может получить md5 если ему передать путь к файлу-картинке?

Akella225
Akella225
09/02/2015 17:31
Ответить на  Akella225

Нашёл

function GetMD5File(const fileName: string) : string;
var
idmd5 : TIdHashMessageDigest5;
fs : TFileStream;
hash : T4x4LongWordRecord;
begin
if not FileExists(fileName) then
begin
result := '';
exit;
end;

idmd5 := TIdHashMessageDigest5.Create;
fs := TFileStream.Create(fileName, fmOpenRead OR fmShareDenyWrite);
try
result := idmd5.HashStreamAsHex(fs) ;
finally
fs.Free;
idmd5.Free;
end;
end;

Александр Усатов

Как-то функция md5 на Синапс работает некорректно или я что-то делаю не так.
Я знаю, что md5(«fc2e73d4fd7dddcd31d28bea5cb2df59:/username/») должен привести к результату: 00a89418c3a4f92d5407e36116117cd9
А у меня получается:
sTempPs:=’fc2e73d4fd7dddcd31d28bea5cb2df59:/username/’;
sBlockPs:=MD5(sTempPs);
#0’Ё”’#$18’Г¤щ-T’#7’гa’#$16#$11’|Щ’
Даже близко не похоже.
Подскажите, что я делаю не так.

Александр Усатов

Через Инди получается:
sTempPs:=’fc2e73d4fd7dddcd31d28bea5cb2df59:/username/’;
with TIdHashMessageDigest5.Create do
try
sTempPs:=AnsiLowerCase(HashStringAsHex(sTempPs));
finally
Free;
end;
а вот Синапс почему-то подводит )