Оба способа позволяют проверять целостность данных и выполнять аутентификацию. Можно использовать хеш, однако это не вполне надежно, поскольку хакер может изменить данные, заново вычислить хеш и добавить его к потоку данных. Опять-таки, факт подмены данных обнаружить не удастся. Если вы решите использовать хеш, хеш с ключом или цифровую подпись, поток зашифрованных данных изменится, как показано на рис. 8-5. Данные, зашифрованные потоковым алгоритмом, восприимчивые к атакам с переворотом бит Хеш, хеш с ключом или цифровая подпись Данные, зашифрованные поточным алгоритмом с проверкой целостности Рис. 85. Потоковое шифрование зашифрованные данные, с проверкой целостности и без нее Что выбрать: хеш, хеш с ключом или цифровую подпись Как я уже говорил, вы вправе вычислить хеш и добавить его в конец зашифрованного сообщения. Но так делать не рекомендуется, поскольку хакер может легко пересчитать хеш после изменения данных. Использование хеша с ключом или цифровой подписи обеспечивает лучшую защиту от модификации данных. Создание хеша с ключом Хеш с ключом (keyed hash) кроме обычного дайджеста сообщения содержит определенные секретные данные, известные только отправителю и получателю. Он обычно создается путем хеширования открытого текста и конкатенацией полученного значения с секретным ключом или его производной. Не зная секретный ключ, невозможно вычислить хеш с ключом. Примечание Хеш с ключом один из типов кода аутентификации сообщения (message authentication code, MAC). Подробнее о нем на Web-странице What are Message Authentication Codes (Что из себя представляют коды аутентификации сообщения) (http:www.rsasecurity.com,rsalabs. faq.html). Часть II Методы безопасного кодирования На рис. 8-6 показана схема процесса шифрования с применением хеша с ключом. Ключ шифрования Функция шифрования Открытый текст MAC-функцияMAC-ключ Шифрованный текст Шифрованный текст MAC MAC Рис. 86. Шифрование сообщения и создание хеша с ключом Создавая хеш с ключом, разработчики часто совершают ошибки. Сейчас я познакомлю вас с наиболее типичными, а затем расскажу, как правильно генерировать хеш с ключом. Не забудьте о ключе Забыть использовать ключ там, где нужен хеш с ключом, наиболее распространенная ошибка. Одного только хеша недостаточно. Никогда не повторяйте подобную ошибку! Не используйте один ключ для шифрования данных и хеша Это вторая по частоте ошибка. Если вы зашифровали данные одним ключом (К), а хеш другим (К), хакеру придется сначала узнать К, чтобы расшифровать данные, и К, чтобы их изменить. Если для обеих целей вы применили только К, то для изменения данных хакеру достаточно вычислить лишь этот ключ. Не создавайте К на основе К Иногда разработчики создают новый ключ, выполняя довольно простые операции над уже имеющимся ключом (например, сдвиг бит). Запомните: хакеру ничего не стоит повторить простую операцию! Создание хеша с ключом Как CryptoAPI, так и классы. NET Framework поддерживают создание хеша с ключом. Далее приведен пример программы на основе CryptoAPI, где хеш с ключом создается по алгоритму (Hash-Based Message Authentication Code). Она есть в папке Secureco\Chapter\MAC. Стандарт алгоритма описан в RFC 2104 (http:www.ietf.org,rfc.rfc.txt).,* MAC.cpp *. #include "stdafx.h" ГЛАВА 8 Подводные камни криптографии 251 tuff(void *szKey, cbKey, void *pbData, cbData, *pb, pcb) dwErr = 0; hProv; hKey; hHash, hKeyHash; try if (! CryptAcquireContext(&hProv, 0, 0, _RSA_, _)) throw; Создаем ключ хеширования. if (! CryptCreateHash(hProv, _SHA, 0, 0, &hKeyHash)) throw; if (! CryptHashData(hKeyHash, ()szKey, cbKey, 0)) throw; if (! CryptDeriveKey(hProv, _DES, hKeyHash, 0, &hKey)) throw; Создаем объект! хеш. if(! CryptCreateHash(hProv, _, hKey, 0, &hHash)) throw; _ hmacInfo; ZeroMemory(&hmacInfo, sizeof(_)); hmacInfo. HashAlgid = _SHA; if(! CryptSetHashParam(hHash, HP__, ()&hmacInfo, 0)) throw; Вычисляем для данных. if(! CryptHashData(hHash, ()pbData, cbData, 0)) throw; Выделяем память и получаем. cb = 0; if(! CryptGetHashParam(hHash, HP_, &cb, 0)) throw; Получаем размер хеша. *pcb = cb; *pb = new,cb.; 252 Часть II Методы безопасного кодирования if (== *pb) throw; if(!