Вы здесь

CA-Visual Objects: использование алгоритмов хеширования (SHA-256, MD5)

ХешированиеАлгоритмы хеширования предназначены для создания «отпечатков» или дайджестов сообщений произвольной длины и последующей проверки их подлинности. Поэтому, часто используются при генерации и проверке паролей, лицензионных ключей, контроле целостности файлов и пр. полезных вещей.
 
Если Вы делаете обновление своих программ через интернет, это тема для Вас.
 
 
Алгоритмов хеширования достаточно много, поэтому расскажу пока о паре: SHA-256 (SHA-2) и MD5. Если Вас интересует, как реализованы алгоритмы CRC32, SHA-1 и пр. – отправляю в википедию или можете смело качать исходники по декодированию архивов, типа WinRAR (UnRAR source code) и пр. Всё есть, надо только желание.
 
Некоторое время назад, в группе VO Фил МакГиннесс (Phil McGuinness) из Sherlock Software (Австралия) выложил полный комплект для реализации алгоритма SHA-256. Я позволил себе чуток его скорректировать (под КаВо 2.8), и изменённую версию кода вы можете скачать в конце этой заметки.
 
В принципе, сам Фил (по наводке друзей по переписке) исходный код взял с сайта «Frez Systems Limited». Там есть ещё много других полезных алгоритмов (например, генерация ключей, распаковка ZIP-архивов), а так же – условия пользования. После этого, он его адаптировал для КаВо и выложил для общего обозрения. И за это ему – большое спасибо!
 
Какое шифрование стоит использовать – Вам решать. Я придерживаюсь старой поговорки, красочно озвученной в кинофильме «Формула любви»: «Что один человек собрал, другой завсегда разобрать сможет». Поэтому, я не доверяю стойкостям алгоритма, надёжности компьютерам, а исхожу из необходимости и практичности. А практика говорят, что пусть алгоритм MD5 ломается, но он более распространён, чем SHA-2, разрешён в коммерции, а главное – более быстр. Поэтому, для проверки целостности файлов – он более чем допустим.
 
Писать код? «Ха-ха» три раза. Всё уже давно за нас сделано. В WinXP и более старших версиях есть прекрасные API-функции (см. cryptdll.dll, advapi32.dll). В качестве основы взял CryptDLL.dll. Прочитав нужную статью на MSDN от Microsoft, написал структуру и три процедуры:
 
STRUCT MD5_CTX
      MEMBER DIM state[ 2 ] AS DWORD
      MEMBER DIM count[ 4 ] AS DWORD
      MEMBER DIM Buffer[ 64 ] AS BYTE // Буфер
      MEMBER DIM digest[ 16 ] AS BYTE // Данные из буфера после MD5Final()
 
_DLL PROC MD5Init( context AS MD5_CTX ) PASCAL:CryptDll.MD5Init
_DLL PROC MD5Update( context AS MD5_CTX, seq AS BYTE PTR, inputLen AS INT ) PASCAL:CryptDll.MD5Update
_DLL PROC MD5Final( context AS MD5_CTX ) PASCAL:CryptDll.MD5Final
 
 
Вот и всё! Ну и как же теперь использовать это «счастье»? А всё очень просто, и не просто-просто, а совсем просто. :)
 
FUNCTION GetMD5( hBuffer AS BYTE PTR, wSize AS DWORD, wBlock := 0 AS DWORD ) AS STRING PASCAL
      // Description: Функция получения MD5-хэша указаной строки
      // Parameters :
//          hBuffer     - указатель на строку, для которой формируем MD5-хэш
//          wSize       - размер этой строки
//          wBlock      - размер читаемых блоков. Если он равен нулю, то формируем MD5-хэш сразу,
//                                       иначе - работаем блоками, указанного размера. Значение по умолчанию - 0.   
      // Returns    : строка MD5-хэша
 
      LOCAL cRet AS STRING
      LOCAL strMD5_CTX IS MD5_CTX
      LOCAL wPos AS DWORD
      LOCAL wRest AS DWORD
 
cRet := NULL_STRING
 
      // Готовим структуру контекста
      MemSet( @strMD5_CTX, 0, _SIZEOF( MD5_CTX ) )
 
      // Инициализируем контекст
      MD5Init( @strMD5_CTX )
      IF PTR( _CAST, @strMD5_CTX ) == NULL_PTR
 
            ErrorBox{, "Ошибка !"}:Show()
            RETURN cRet
      ENDIF
 
      // Формируем контекст
      IF wBlock = 0 .OR. wBlock > wSize
 
            // Формируем сразу и полностью
            MD5Update( @strMD5_CTX, @hBuffer[ 1 ], INT( _CAST, wSize ) )
      ELSE
 
            // Обновляем контекст, добавляя к нему блоки данных
wRest := wSize
            FOR wPos := 1 UPTO wSize STEP wBlock
 
                  IF wRest >= wBlock
 
                        wRest -= wBlock
                        MD5Update( @strMD5_CTX, @hBuffer[ wPos ], INT( _CAST, wBlock ) )
                  ELSE
                        MD5Update( @strMD5_CTX, @hBuffer[ wPos ], INT( _CAST, wRest ) )
                  ENDIF
            NEXT wPos
      ENDIF
 
      // Финализируем контекст. Теперь он содержит результат
      MD5Final( @strMD5_CTX )
 
      // Получаем строковое представление MD5-хеша
      FOR wPos := 1 UPTO 16
 
            cRet += IntToHex( strMD5_CTX.digest[ wPos ], 2 ) // ANSI: 255 => FF (2 буквы)
      NEXT wPos
 
      RETURN cRet
 
И, тогда код в программе будет выглядеть так:
 
hText := String2Psz( "Hello World !" )
cMD5 := GetMD5( hText, PszLen( hText ) )
 
 
Просто замечательно! А как поступить с файлом (для контроля целостности)? Тут чуток сложнее. Файл надо весь прочесть и просчитать. И для этого я написал функцию отображения файла в память: файл открыли, сделали образ в памяти и закрыли. А потом, с образом делаем, что хотим:
 
FUNCTION MapFile_ReadOnly( cFileName AS STRING, dwFileSize AS USUAL ) AS BYTE PTR PASCAL
      // Description: Функция создаёт и возвращает указатель на проекцию файла, и его размер.
                        //          Как следует из названия, файл открывается только для чтения, монопольно.
      // Parameters :
      // Returns    : Указатель на проекцию файла
 
      LOCAL hMap AS BYTE PTR
      LOCAL hFile, hFileMap AS PTR
 
// Открываем файл
      hFile := FxOpen( cFileName, OF_READ + OF_SHARE_EXCLUSIVE, NULL_STRING )
      IF ( hFile == INVALID_HANDLE_VALUE )
 
            RETURN hMap
      ENDIF
 
// Меряем
      dwFileSize := GetFileSize( hFile, NULL_PTR )
      IF dwFileSize = 0
 
            FClose( hFile )
            hFile := NULL_PTR
 
            RETURN hMap
      ENDIF
 
// Создаём проекцию в память для нашего файла
      hFileMap := CreateFileMapping( hFile, NULL_PTR, PAGE_READONLY, 0, dwFileSize, NULL_PTR )
 
// Закрываем файл
      FClose( hFile )
      hFile := NULL_PTR
     
      IF hFileMap == NULL_PTR .OR. GetLastError() == ERROR_ALREADY_EXISTS
 
            RETURN hMap
      ENDIF
 
// Отображаем проекцию в адресное пространство вызывающего процесса
      hMap := MapViewOfFile( hFileMap, FILE_MAP_READ, 0, 0, dwFileSize )
 
      CloseHandle( hFileMap )
      hFileMap := NULL_PTR
 
      RETURN hMap
 
 
Осталось проверить результат. Можно, конечно, сделать вручную, по известному алгоритму. А можно и с помощью какой-то программки. В интернете есть много чего. Я использовал бесплатную с сайта ImplBits. Если Вас заинтересовало моё решение – внизу заметки можно скачать полный пример работы с MD5. Всем спасибо и до новых встреч!
 
Жду Ваших комментариев. :)
 
 
31.08.2013 г.   Карандаш.
 
P.S.: 06.10.2014 г.
Исправлена функция MapFile_ReadOnly(). Соответственно, загружен новый пример App_MD5_0002.rar
 
ВложениеРазмер
Файл SHA-256_0001.rar5.51 КБ
Файл App_MD5_0002.rar3.63 КБ

Theme by Danetsoft and Danang Probo Sayekti inspired by Maksimer