Результирующий поток байт хешируется по SHA-1, чтобы получить 20-байтное значение инициирования счетчика (seed value), которое применяется для генерации случайных чисел в соответствии со стандартом 186-2, приложение 3.1. Часть II Методы безопасного кодирования Разработчик вправе обеспечить большую степень энтропии, предоставляя буфер данных (подробно о предоставляемых пользователем буферах описано в документации к CryptGenRandom в Platform SDK). Итак, если пользователь предоставил дополнительные данные в буфер, они становятся дополнительным ингредиентом колдовского варева, на основании которого генерируются случайные числа. Простейшая форма вызова CryptGenRandom выглядит так: #include windows.h #include wincrypt.h M hProv = ; fRet = ; pGoop; cbGoop = sizeof pGoop; if (CryptAcquireContext(&hProv, _RSA_, _)) if (CryptGenRandom(hProv, cbGoop, &pGoop)) fRet = ; if (hProv) CryptReleaseContext(hProv, 0); Однако показанный далее класс C++ CCryptRandom более эффективен, поскольку вызовы CryptAcquireContext (занимает много времени) и CryptReleaseContext, которые соответственно создают и разрушают ссылки на криптографический провайдер (Cryptographic Service Provider, CSP), инкапсулированы в конструкторах и деструкторах класса. Поэтому, пока существует объект класса CcryptRandom, генерация не создаст заметной нагрузки на систему.,* CryptRandom.cpp *. #include windows.h #include wincrypt.h #include iostream.h class CCryptRandom public: CCryptRandom(); virtual ~CCryptRandom(); get(void *lpGoop, cbGoop); private: m_hProv; }; CCryptRandom:CCryptRandom() m_hProv = ; CryptAcquireContext(&m_hProv, ГЛАВА 8 Подводные камни криптографии 229 _RSA_, _); if (m_hProv ==) throw GetLastError(); } CCryptRandom:~CCryptRandom(), if (m_hProv) CryptReleaseContext(m_hProv, 0); CCryptRandom:get(void *lpGoop, cbGoop) if (! m_hProv) return ; return CryptGenRandom(m_hProv, cbGoop, reinterpret_cast(lpGoop)); } void main() try CCryptRandom r; Сгенерировать 10 случайных чисел из диапазона 099. for (int i=0; i; i++), d; if (r.get(&d, sizeof d)) cout d % 100 endl; } catch (), обработка исключений. } Код этого примера есть в папке Secureco\Chapter. Определить следующее случайное число, генерируемое CryptGenRandom, практически невозможно как раз то, что нам надо! Совет Из соображений производительности избегайте частых вызовов CryptAcquireContext, а возвращенный описатель рекомендуется передавать в любое место приложения, в том числе в другие потоки. Что такое 140-1 Федеральный стандарт обработки информации 140-1 (Federal Information Processing Standard) применяется для сертификации криптографических продуктов. В нем описаны стандартные реализации некоторых широко используемых алгоритмов. Подробнее о 140-1 на Web-странице http: www.microsoft.com,technet.securityaq.asp. Также помните, что если планируется продавать ПО правительству США, то необходимо использовать алгоритмы, соответствующие стандарту 140-1. Легко догадаться, что rand не входит в их число. Стандартная версия CryptGenRandom в Windows 2000 и более поздних версиях удовлетворяет требованиям. Часть II Методы безопасного кодирования Случайные числа криптографического качества в управляемом коде Если вам требуется создать безопасные случайные числа криптографического качества в управляемом коде, никогда не делайте так, как показано далее, поскольку здесь вызывается линейно согласующаяся функция, подобная rand библиотеки C: Генерация нового ключа шифрования. byte key = new byte; new Random(). NextBytes(key); А как делать надо, показано в этом фрагменте на C#, где 32-байтный буфер заполняется надежными с точки зрения криптографии случайными данными: using System. Security. Cryptography; try byte b = new byte; new ryptoServiceProvider(). GetBytes(b); выводим результат. for (inti=0;i b. Length; i++) Console. Write(" ", b,i. ToString("x")); } catch(CryptographicException e), Console. WriteLine(e. Message); Класс ryptoServiceProvider обращается к CryptoAPI, вызывая функцию CryptGenRandom, которая генерирует случайные данные. Этот же пример на Visual Basic.