Шаг 18 - Генерация хешей MD2, MD4, MD5 с помощью OpenSSL

Иногда хочется иметь готовый код использования какой-нибудь функции, а найти его не получается... Поэтому приходится писать свою собственную документацию.

Набор хеш-функций MD2, MD4 и MD5 в библиотеке OpenSSL используется для вычисления хешей данных. Для тех, кто не знает, что такое хеш, возможно будет более понятным термин контрольная сумма или чексумма (от англ. checksum). Это число генерируется на основе содержимого данных и используется в основном для проверки целостности данных. Все алгоритмы вычисления хешей разрабатывались таким образом, чтобы даже при изменении одного бита данных результирующий хеш менялся до неузнаваемости, по сравнению с первоначальным. Причем по хешу данных (по понятным причинам необратимости работы алгоритма) предсказать содержимое данных невозможно.

root@localhost:/home/dron# echo -n "Arnold Schwarzenegger" | md5sum
c8d96c6f9a91ba729ca1688f90f6d2b7  -
root@localhost:/home/dron# echo -n "Brnold Schwarzenegger" | md5sum
c0707d286aaadc1a8c1f5e77cb5a4961  -

Этим свойством можно пользоваться для реализации процесса аутентификации без передачи идентификационных данных по сети в открытом виде. Наиболее наглядным примером использования хеш-функции MD5 является реализация алгоритма защищенной проверки пароля APOP в протоколе POP3. Все тонкости работы алгоритма я описывать не буду, только основные моменты и сам принцип.

Реализация APOP в POP3 позволяет произвести проверку пароля между сервером и клиентом без передачи пароля в открытом виде. Идея проста и заключается в том, что обе стороны (сервер и клиент) изначально пароль знают, да в общем-то другого и быть не может :) Сервер во время подключения клиента к своему приглашению добавляет слово-сальт (словом salt в терминах Юникса обозначается префикс используемый для шифрования паролей DES). Сальт меняется сервером периодически, для этого можно использовать текущее время в секундах, идентификатор процесса или просто случайное число. Это слово-сальт клиент добавляет к паролю и потом передает хеш-функцию MD5 от полученной строки серверу. Сервер производит такую же операцию и сравнивает результат работы MD5 с переданным хешем от клиента. Таким образом получается, что пароль в открытом виде не передается, а по передаваемому хешу практически не возможно подобрать пароль.

Библиотека OpenSSL для работы с хешами предоставляет множество различных по скорости работы и криптостойкости алгоритмов. Я приведу пример работы с семеством Message-Digest (MD) алгоритмов, в которое входят алгоритмы MD2, MD4 и MD5. Набор функций для работы с ними одинаковы и отличаются соответственно названиями:

Для работы с этими алгоритмами необходимо подключить соответствующий заголовочный файл, например для MD5:


#include <openssl/md5.h>

Выработка хеша делается следующим образом (для примера возьмем последовательность 0123456789):


#include <stdio.h>
#include <stdlib.h>
#include <openssl/md5.h>

int main() {
	int i;
	MD5_CTX md5handler;
	unsigned char md5digest[MD5_DIGEST_LENGTH];

	MD5_Init(&md5handler);
	MD5_Update(&md5handler, "01234", 5);
	MD5_Update(&md5handler, "56789", 5);
	MD5_Final(md5digest, &md5handler);

	for (i=0; i < MD5_DIGEST_LENGTH; i++) {
		printf("%02x", md5digest[i]);
	};

	printf("\n");
	return 0;
};

Компилируем:

gcc main.c -lcrypto

Пробуем:

root@localhost:/home/dron# ./a.out
781e5e245d69b566979b86e28d23f2c7

Для проверки результата работы можно использовать команду md5sum:

root@localhost:/home/dron# echo -n "0123456789" | md5sum
781e5e245d69b566979b86e28d23f2c7  -

Как видите совпадает. Этим набором функций можно хешировать большие объемы данных последовательно вызывая MD5_Update:


void MD5_Update(MD5_CTX *c, const void *data,
		unsigned long len);

Но, если Вам требуется хешировать небольшие объемы данных целиком помещающиеся в буфер, то можно пользоваться упрощенной функцией:


unsigned char *MD5(const unsigned char *d, unsigned long n,
		unsigned char *md);

Пример:


#include <stdio.h>
#include <stdlib.h>
#include <openssl/md5.h>

int main() {
	int i;
	MD5_CTX md5handler;
	unsigned char md5digest[MD5_DIGEST_LENGTH];

	MD5("0123456789", 10, md5digest);

	for (i=0; i < MD5_DIGEST_LENGTH; i++) {
		printf("%02x", md5digest[i]);
	};

	printf("\n");
	return 0;
};

Получается более компактное и красивое решение.

Использовать алгоритмы MD2 и MD4 можно аналогично, хотя сегодня они практически нигде не применяются, так как являются более старыми версиями.


Предыдущий Шаг | Следующий Шаг | Оглавление
Автор Кузин Андрей - 14.09.2006