crypt — Необратимое шифрование (хэширование)


Содержание

Шифрование с использованием crypt()

В настоящее время я использую очень безопасную систему входа в систему, но я новичок в функции crypt() и нуждаюсь в быстрой помощи.

Я использовал crypt() для шифрования строки пароля во время регистрации и сохранения ее в базе данных. Однако как я смогу расшифровать ключ во время входа? Или как я должен делать иначе? Или, возможно, можно было бы сделать магию с представленной строкой пароля, чтобы сравнить ее с зашифрованным ключом в базе данных?

crypt() не шифрует пароли, он их хэширует. Основное различие заключается в том, что вы не можете вернуть хэшированные пароли (подумайте о хэш-коричневых тонах — если у вас есть хеш-коричневые цвета, вы не сможете вернуть картофель).

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

Прочитайте документацию на crypt() , чтобы понять, что «волшебство» за кодом выше работает.

crypt() предоставленный пароль при входе в систему. Сравните вывод с предыдущим выходом crypt() . Если они совпадают, пароли соответствуют.

Это основная теория работы односторонней хэш-функции.

Не шифруйте пароль. Вместо этого сохраните его с помощью хэша.

И так вы должны это делать. Обратите внимание, что этот код — это то, как я это сделал, и вы можете изменить некоторые из вещей. И вы должны определить свою собственную уникальную соль, будь то в файле конфигурации или иначе. Он должен либо a) находиться в глобальном масштабе, как я опубликовал, либо вы можете изменить его, чтобы определить его в функции. Также вы не шифруете, вы на самом деле хешируете. Шифрование — оба способа, хеширование — это шифрование одним способом. Это означает, что вы не можете расшифровать хэш. Вы можете только грубо угадать оригинальный текст.

Как указано в коде, лицензия CC-By, поскольку я считаю, что это достаточно хорошо для большинства вещей. Кроме того, держите блок таким же, как ссылка на эту страницу, как и то, что я делаю со всем своим исходным кодом. Также я понимаю, что «случайная» строка на самом деле не такая случайная, но она достаточно случайна для использования вами с той целью, в которой она будет.

Изменить 2: Также избегайте имени пользователя. Я не избегаю пароля, так как я его хэширую, поэтому избежать этого не нужно, поскольку он уже смягчен и будет просто тратить циклы. Но только если вы делаете что-то подобное. Обязательно избегите имени пользователя mysql_real_escape_string. Если вы используете php5 +, вы должны изучить mysqli (если вы используете mysql). Если вы используете другую систему, вам придется искать ее самостоятельно, так как я знаю только mysql. Я уйду на пару дней, поэтому я действительно надеюсь, что это сработает для вас. Я проверю его время от времени, но я могу забыть. так что да. Надеюсь, это поможет вам, так как это безопасно, безопасно и должно хорошо работать для вас.

Изменить 3: Изменена функция случайных строк, чтобы сделать ее немного сильнее, так как я забыл, что это будет использовать для генерации временных паролей. Это должно сделать его достаточно случайным для использования для этой цели, поскольку иначе сгенерированный пароль может быть известен кем-то, зная точное время (с текущим микротрейдом), хотя это маловероятно, это все еще делает его немного сильнее и должно сделать это от таких видов атак. Он не должен быть полностью готовым к производству и должен быть защищен для вашей системы. Просто не забудьте установить переменную $unique_salt somwhere в глобальной области или установить ее каждый раз, когда она используется в каждой из этих функций.

Crypt — Необратимое шифрование (хэширование)

Компонуется при указании параметра -lcrypt.

ОПИСАНИЕ

key — задаваемый пользователем пароль.

salt (соль) — двухсимвольная строка, выбираемая из набора [a-zA-Z0-9./]. Эта строка используется для направления алгоритма по одному из 4096-и путей.

Если взять младшие 7 битов каждого из первых 8 символов key, то получается 56-битный ключ. Этот ключ используется для многократного шифрования константной строки (обычно строки, состоящей из символов «0»). Возвращаемое значение — указатель на зашифрованный пароль, серия из 13-и печатных ASCII-символов (первые два символа содержат salt). Возвращаемое значение указывает на статические данные, которые перезаписываются при каждом вызове.

Предупреждение: количество ключей равно 2**56 т. е. существует 7.2e16 возможных вариантов. Полный перебор этого множестве возможен с помощью большого количества параллельных компьютеров. Программное обеспечение, такое, как crack(1), способно отыскать часть ключей из этого множества, обычно используемых людьми для создания пароля. Поэтому в качестве пароля не стоит, как минимум, использовать простые слова и имена. Рекомендуется использовать программу passwd(1), которая проверяет сложность пароля уже на стадии ввода.

Алгоритм DES имеет некоторые особенности, которые не позволяют использовать интерфейс crypt() для чего-то кроме аутентификации пользователя по паролю. Если вы планируете использовать интерфейс crypt() в проекте шифрования, то лучше не делайте этого. Вместо этого возьмите хорошую книгу по шифрованию или одну из общедоступных библиотек DES.

Функция crypt_r() является реентерабельной версией crypt(). Для учёта и хранения результата в ней используется структура, на которую указывает data. Перед первым вызовом crypt_r() требуется выделить место под структуру и присвоить data->initialized значение 0.

Хеширование, шифрование и цифровая подпись с использованием CryptoAPI и .NET

Автор: Алексей Остапенко
The RSDN Group
Источник: RSDN Magazine #1

Опубликовано: 09.10.2002
Исправлено: 15.04.2009
Версия текста: 1.0

Введение

Криптография, как прикладная дисциплина, существует уже очень давно. Один из простейших шифров, шифр алфавитной замены, использовался еще во времена Цезаря. Но настоящий расцвет криптографии произошел только в последние несколько столетий, когда к задачам шифрования был применен математический аппарат. Эта статья дает обзор криптографических алгоритмов, предоставляемых MS CryptoAPI 2.0 и .NET Framework, и содержит примеры их использования на языках C++ (CryptoAPI) и C# (.NET). Также вкратце описывается набор замечательных ошибок, обнаруженных автором в реализации класса RSACryptoServiceProvider библиотеки .NET Framework.

Симметричные и асимметричные шифры

Основное назначение любого шифра – обеспечение возможности передачи сообщения по незащищенным каналам (не обязательно сетевым) с защитой этого сообщения от прочтения посторонними лицами. Посмотрим, что же такое шифры и с чем их едят.


Сначала дадим краткое определение криптографических функций шифрования и дешифрования.

Пусть есть две функции E(M1,K1) и D(M2,K2), зависящие от сообщений M1 и M2 и ключей K1 и K2, такие, что D(E(M,K1),K2)=M для некоторой пары ключей K1 и K2 и любого M, тогда E(.) и D(.) – функции шифрования и дешифрования, соответственно. Результат C=E(M,K1) называется криптограммой (или шифрограммой).

К функциям E(.) и D(.) предъявляются следующие требования:

  1. Они должны быть легко вычислимы для любых M, если известны K1 и K2.
  2. Вычисление D(M,?) – тяжелая задача при неизвестном ключе K2 (т.е., не зная ключа дешифрования, мы не можем вычислить исходное сообщение по криптограмме).
  3. Вычисление ключей K1 и K2 – тяжелая задача при наличии некоторого набора пар (имея набор криптограмм и исходных сообщений, мы не можем вычислить ключи).
  4. Вычисление K2 (в случае K2 отличного от K1) при известном K1 – также должно быть трудной задачей (это требование относится к асимметричным шифрам. Для цифровой подписи K2 и K1 меняются ролями).

Надежность шифра определяется именно по его соответствию вышеперечисленным требованиям, при этом считается, что алгоритмы вычисления E(.) и D(.) известны всем (есть еще вариант так называемой Security by obscurity, т.е. защиты из-за неизвестности алгоритма, но он при оценке стойкости шифров не рассматривается).

Если K1=K2, то шифр называется симметричным, в противном случае – асимметричным. Примеры известных симметричных шифров – DES, IDEA, Blowfish, ГОСТ, асимметричных – RSA, ECC.

Шифры также бывают блочными и потоковыми. Блочный шифр работает с сообщениями фиксированного размера (например, 64 бита), а поточный – шифрует весь поток данных посимвольно (например, побайтно). Известные блочные шифры – DES, IDEA, Blowfish, потоковые – RC4. Блочность шифра не означает невозможность шифрования им сообщений, превышающих по длине размер блока.

Существуют следующие распространенные варианты использования блочных шифров для шифрования длинных сообщений:

  • ECB (Electronic CodeBook) – режим электронной кодовой книги. В этом режиме каждый блок шифруется независимо от других. Этот режим удобен для шифрования частей файла, записей в базе данных и т.п., однако при этом одинаковые фрагменты исходного сообщения порождают одинаковые фрагменты криптограммы, так что, зная часть исходного сообщения, злоумышленник может легко восстановить совпадающие фрагменты.
  • CBC (Cipher Block Chaining) – режим сцепления блоков шифра. Это самый распространенный режим. В этом режиме шифруется не сам блок исходного сообщения, а его XOR с результатом шифрования предыдущего блока (это верно для всех блоков, кроме первого). Т.е. C(i)=E(M(i) xor C(i-1)), для всех i>0, C(0)=E(M(0) xor I), где I – это начальный вектор инициализации (он может передаваться вместе с шифрограммой в открытом виде).
  • CFB (Cipher FeedBack) – режим обратной связи по шифру. Этот режим фактически превращает блочный шифр в потоковый. Для шифрования используется регистр R с размером, равным размеру блока. В начале он заполняется инициализационным вектором I. На каждом шаге регистр шифруется, и над n битами исходного сообщения (шифрование может идти как побитно, так и блоками по n=2, 4, … бита) выполняется XOR со старшими (левыми) битами E(R). Результат этого XOR и является частью шифрограммы. Затем R сдвигается на n бит влево, а результат XOR записывается в младшие (правые) разряды R.
  • OFB (Output FeedBack) – режим выходной обратной связи. Этот режим очень похож на CFB, но вместо битов шифрограммы в регистр R вдвигаются старшие биты результата E(R), тем самым состояние сдвигового регистра вообще не зависит ни от исходного сообщения, ни от криптограммы.

Потоковые шифры в этой статье не рассматриваются. Я также не буду приводить деталей реализации конкретных блочных шифров, т.к. это выходит за рамки данной статьи, и существуют хорошие книги по криптографии, где эти алгоритмы рассматриваются в деталях (например, [1]).

Однако стоит немного остановиться на асимметричных шифрах, а точнее на популярной их разновидности – шифрах с открытым ключом. Шифр с открытым ключом имеет два ключа – открытый (public), который можно свободно распространять, и закрытый (private), который держится в секрете. Зашифровать сообщение может кто угодно, но расшифровать его сможет только владелец закрытого ключа. Системы шифрования с открытым ключом, как правило, основываются на сложности разрешения какой-либо математической задачи. Например, популярный алгоритм RSA основан на сложности разложения на множители произведения двух больших простых чисел. Рассмотрим этот алгоритм поподробнее, т.к. понимание основ его работы нам пригодится в дальнейшем.

Алгоритм RSA

Для генерации ключевой пары выбираются два больших (по современным требованиям надежности не менее 512 бит) простых числа (некоторые популярные алгоритмы их генерации рассмотрены в [1]) p и q . Затем вычисляется их произведение n = p * q и выбирается случайное число e , так что e взаимно просто с ( p -1)*( q -1). Также вычисляется число d , такое, что d * e =1 mod ( p -1)*( q -1) (эта операция делается с помощью расширенного алгоритма Евклида, см. [1]). Это означает, что

d * e =1+k*( p -1)*( q -1), где k – некое целое число. Открытым ключом является пара e и n , закрытым – d и n . Алгоритм позволяет шифровать сообщения m меньшие, либо равные n (как целые неотрицательные числа). Большие сообщения следует разбивать на части.

Шифрование осуществляется следующим способом:

c =m^ e mod n (возведение m в степень e по модулю n ),

а дешифрование производится так:

Гарантию того, что получится верный результат, дает малая теорема Ферма. Для любого a меньше, либо равного n:

a^ phi(n) =1 mod n , где phi(n) – число целых положительных чисел меньших n и взаимно простых с ним. Для n = p * q , phi(n)= ( p -1)*( q -1) и

(m^ e )^ d mod n = m^(1+k* phi(n) ) mod n=m.

Основное предназначение криптографических хешей – контроль подлинности данных путем вычисления от них некоторой функции H(.), дающей результат фиксированной (и обычно небольшой длины). Функция H(.) должна удовлетворять следующим требованиям:

Для любых сообщений m, h=H(m) легко вычисляема.

Задача нахождения такого u (отличного от m), чтобы H(u)=h, должна являться трудной при неизвестном m.

Задача нахождения такого u, что H(u)=H(m) является трудной при известном m.

Большинство популярных хеш-функций генерируют хеш длиной 128 бит и более. Примерами наиболее распространенных хеш-функций являются MD5 и SHA. Значения хеш-функций часто используются в системах электронной цифровой подписи для генерации дайджеста сообщения, который затем и подписывается тем или иным алгоритмом. Также хеш-функции применяются в системах аутентификации для проверки паролей – открытый пароль пользователя не должен храниться в системе, вместо него хранится его хеш, который затем и сравнивается с хешем от пароля, вводимого пользователем при входе в систему.

Цифровая подпись

Электронная цифровая подпись (ЭЦП), как и обычная, позволяет установить некую отметку, указывающую на принадлежность электронного сообщения конкретному автору. Алгоритмы цифровой подписи тесно связаны с асимметричными шифрами. Например, алгоритм цифровой подписи RSA – это практически шифр RSA, но шифруется не само сообщение, а его дайджест, и шифрование производится не на открытом ключе, а на закрытом. В этом случае любой получатель, имеющий открытый ключ автора, может расшифровать дайджест и проверить его правильность.


Обмен ключами

Хорошо, у нас есть работающий шифр (или система ЭЦП), который мы хотим использовать для защищенной передачи данных. Однако остается одна проблема – обмен ключами. Симметричному алгоритму нужен один и тот же ключ как на шифрующей, так и на дешифрующей стороне. Требуется надежный способ генерации одинаковых сеансовых ключей на обеих сторонах или передачи ключа шифрующей стороной дешифрующей стороне (в качестве такого способа может выступать надежный курьер с дискеткой). Существуют специальные алгоритмы генерации совпадающих сеансовых ключей, но мы их рассматривать не будем, т.к. в CryptoAPI и .NET они не используются. Рассмотрим, как можно передать сгенерированный на шифрующей стороне сеансовый ключ другой стороне. Самый простой вариант – использовать шифр с открытым ключом. Имея открытый ключ получателя, мы шифруем на нем сеансовый ключ и передаем результат получателю вместе с зашифрованными данными. Теперь получатель может расшифровать сеансовый ключ и сами данные. Казалось бы, все хорошо, однако тут кроется еще одна проблема, связанная с распределением открытых ключей. В самом деле, где гарантия того, что полученный открытый ключ действительно принадлежит конкретному получателю? Если злоумышленник перехватил оригинальный открытый ключ и подсунул вместо него свой, то зашифрованный сеансовый ключ получит именно он, а не требуемый получатель. В дальнейшем злоумышленник может выдать себя за получателя и перехватить передаваемое сообщение. Для предотвращения такой возможности вводят различные сети доверия (trusted network) и авторитетные источники (trusted authority), которые берут на себя ответственность за подлинность открытого ключа (ключ подписывается собственной подписью центра, которой все безоговорочно доверяют).

Работа с CryptoAPI


Криптопровайдеры, инициализация и деинициализация

Любой сеанс работы с CryptoAPI начинается с инициализации (получения контекста). Инициализация выполняется при помощи функции CryptAcquireContext . В качестве параметров эта функция принимает имя контейнера ключей, имя криптопровайдера, тип провайдера и флаги, определяющие тип и действия с контейнером ключей и режим работы криптопровайдера:

Криптопровайдер – это сущность (обычно библиотека), реализующая определенный набор криптографических алгоритмов и обеспечивающая работу с ними. Существует около семи стандартных провайдеров, предустановленных в системе. Нам для примеров понадобятся два из них – Microsoft Base Cryptographic Provider (MS_DEF_PROV) и Microsoft Enhanced Cryptographic Provider (MS_ENHANCED_PROV) .

ПРИМЕЧАНИЕ

Заметим, что Enhanced-провайдер присутствует только на тех машинах, где установлена поддержка 128-битного шифрования (она автоматически устанавливается вместе с Internet Explorer 6.0).

Каждый криптопровайдер относится к определенному типу. Это позволяет, перебрав все установленные на машине провайдеры, выбрать те, которые поддерживают нужные алгоритмы. Два упомянутых провайдера имеют тип PROV_RSA_FULL .

Криптопровайдер поддерживает защищенные области, называемые контейнерами ключей. Контейнеры позволяют приложениям сохранять и использовать в дальнейшем сгенерированные один раз ключи, обеспечивая защиту самого ключа от злоумышленника.

ПРИМЕЧАНИЕ

Стандартные криптопровайдеры хранят ключи на диске, в зашифрованном виде. Однако существует потенциальная возможность, что злоумышленник, укравший компьютер или жесткий диск, сможет расшифровать сохраненные ключи.

Контейнеры бывают двух типов – пользовательские (этот тип используется по умолчанию) и машинные ( CRYPT_MACHINE_KEYSET ). Пользовательский контейнер доступен только приложениям, выполняемым от имени владельца контейнера. Приложение может использовать такой контейнер для сохранения персональных ключей. Доступ к машинным контейнерам разрешен только администраторам. В них обычно сохраняются ключи, используемые сервисами и системными программами. Тип контейнера задается флагом при получении контекста.

Для первоначального создания контейнера нужно вызвать CryptAcquireContext с флагом CRYPT_NEWKEYSET . Для удаления контейнера требуется указать флаг CRYPT_DELETEKEYSET .

Если приложению не требуется доступ к контейнеру ключей (например, приложение вычисляет хеш MD5), то стоит вызывать CryptAcquireContext с флагом CRYPT_VERIFYCONTEXT , передавая NULL вместо имени контейнера.

Следующий пример демонстрирует инициализацию CryptoAPI для последующего вычисления хеша MD5:

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

Генерация ключей и обмен ключами

Для генерации ключей в CryptoAPI предусмотрены две функции – CryptGenKey и CryptDeriveKey . Первая из них генерирует ключи случайным образом, а вторая – на основе пользовательских данных. При этом гарантируется, что для одних и тех же входных данных CryptDeriveKey всегда выдает один и тот же результат. Это способ генерации ключей может быть полезен для создания симметричного ключа шифрования на базе пароля. Мы же более подробно остановимся на функции CryptGenKey , которая используется чаще всего. Эта функция имеет прототип:

Первый и четвертый параметры говорят сами за себя. Вторым параметром передается идентификатор алгоритма шифрования, для которого генерируется ключ (например, CALG_3DES ). При генерации ключевых пар RSA для шифрования и подписи используются специальные значения AT_KEYEXCHANGE и AT_SIGNATURE . Третий параметр задает различные опции ключа, которые зависят от алгоритма и провайдера. Например, старшие 16 битов этого параметра могут задавать размер ключа для алгоритма RSA. Подробное описание всех флагов можно найти в MSDN. Здесь я упомяну только один флаг — CRYPT_EXPORTABLE, который позволяет экспортировать закрытые ключи RSA из контейнера (по умолчанию это запрещено). Для других ключей этот параметр смысла не имеет – открытые ключи являются свободно экспортируемыми, а сессионные ключи вообще не хранятся внутри контейнера, так что их обязательно нужно экспортировать.

Параметры, которые нельзя задать при генерации ключа (например, инициализационные векторы), можно установить уже после его создания с помощью функции CryptSetKeyParam . Ее рассмотрение, однако, выходит за рамки этой статьи.

Обмен ключами в CryptoAPI реализуется с помощью функций CryptExportKey и CryptImportKey , имеющих следующие прототипы:

В качестве ключей экспорта/импорта могут использоваться либо ключевая пара RSA (с типом AT_KEYEXCHANGE), либо симметричный сеансовый ключ. Параметр dwBlobType зависит от того, какой ключ экспортируется (импортируется), и задает тип структуры, в которую помещается экспортируемый ключ. Для открытого ключа это PUBLICKEYBLOB, и ключ экспорта/импорта при этом лишен смысла и должен быть нулем. Для закрытого ключа это PRIVATEKEYBLOB , и в качестве ключа экспорта может использоваться сеансовый ключ. Для сеансового ключа это обычно SIMPLEBLOB (бывает еще OPAQUEKEYBLOB и SYMMETRICWRAPKEYBLOB , но мы их рассматривать не будем), а экспортируется он, как правило, на открытом ключе получателя.

Описание флагов можно найти в MSDN. Я выделю среди них флаг CRYPT_OAEP, который заставляет криптопровайдера использовать формат PKCS #1 версии 2 при сохранении сессионного ключа с шифрованием RSA. Ключ, сохраненный в этом формате, затем может быть расшифрован другими системами шифрования (например, я так передавал ключ в библиотеку Crypto++). Если же этот флаг не указан, то ключ сохраняется в каком-то ведомом только CryptoAPI формате, и использовать его где-либо еще вряд ли удастся.

И, наконец, параметры pbData и pdwDataLen задают адрес и размер буфера под структуру экспортируемого ключа. Если размер структуры не известен при написании программы, то можно определить требуемый размер буфера, установив в pbData ноль. В этом случае нужный размер возвращается в pdwDataLen .

После окончания работы с ключом, его нужно уничтожить вызовом CryptDestroyKey .

ПРИМЕЧАНИЕ

При этом закрытый ключ сохраняется в контейнере (если, конечно, не использовался режим CRYPT_VERIFYCONTEXT), а сессионные ключи уничтожаются совсем.

В качестве примера рассмотрим создание и экспорт пары ключей для шифрования RSA и симметричного ключа 3DES:


Рабочие примеры генерации и импорта/экспорта ключей приведены в демонстрационных проектах rsakg и encfile.

Симметричные шифры DES и 3DES

Это одни из немногих симметричных шифров, предоставляемых стандартными криптопровайдерами CryptoAPI. Поскольку DES и 3DES – это практически один и тот же алгоритм (3DES – это DES, применяемый 3 раза подряд), то мы ограничимся примером использования алгоритма 3DES.

ПРИМЕЧАНИЕ

Однако заметим, что для использования алгоритма 3DES требуется Enhanced провайдер, а для DES вполне достаточно Base. Впрочем, DES уже не является стойким по современным меркам алгоритмом, поэтому использовать его стоит лишь там, где надежность шифрования не очень критична.

Алгоритм 3DES использует разные ключи DES для каждой из своих итераций. Поэтому размер его ключа равен тройному размеру ключа DES, т.е. 192 (64*3) бита. Реально размер ключа 3DES – 168 (56*3) бит, т.к. в DES один байт ключа является контрольным для основных семи. Шифрование и дешифрование выполняются с помощью функций:

Параметр hHash позволяет параллельно с шифрованием/дешифрованием проводить хеширование данных для последующей электронной подписи или ее проверки. Флаг Final определяет, является ли шифруемый блок данных последним. Он необходим, поскольку данные можно шифровать по кускам, но для последнего блока всегда выполняется определенная деинициализация алгоритма (освобождаются внутренние структуры), и многие алгоритмы производят добавление (и проверку корректности при дешифровании) заполнителя (padding) после основных данных. Параметры pbData и pdwDataLen задают адрес буфера и размер шифруемых данных. Для не последнего блока данных ( Final =FALSE) размер данных должен быть всегда кратен размеру шифруемого алгоритмом блока (для 3DES и DES этот размер равен 64 битам). Для последнего блока допускается нарушение этого условия.

ПРИМЕЧАНИЕ

Заметим, что зашифрованные данные помещаются в тот же самый буфер поверх исходных.

Последний параметр функции CryptEncrypt – dwBufLen может показаться странным. Зачем нам размер буфера, если мы и так знаем размер входных данных? Однако на самом деле он необходим. Как я уже упомянул, многие алгоритмы добавляют заполнитель в последний блок после основных данных. В этом случае размер зашифрованных данных может оказаться больше, чем размер исходных данных. И результат может попросту не вместиться в буфер! Поэтому стоит заранее указать размер буфера, превышающий максимальный размер помещаемых в него открытых данных. Для DES и 3DES максимально возможный довесок составляет 64 бита, т.е. 8 байт (это я установил опытным путем).

В качестве примера шифрования приведу выдержку из демонстрационного проекта encfile:

Асимметричный шифр RSA

Начиная с Windows 2000 расширенный (Enhanced) криптопровайдер поддерживает прямое шифрование данных по алгоритму RSA. Максимальный размер данных, которые можно зашифровать за один вызов CryptEncrypt , равен размеру ключа минус 11 байт. Дело в том, что при шифровании добавляется обязательный заполнитель (padding), который впоследствии проверяется при дешифрации. Соответственно, использование шифра RSA может быть целесообразно только при небольших объемах шифруемых данных (например, при обмене ключами) из-за существенного увеличения объема шифрованного текста и относительно медленной работе алгоритма RSA по сравнению с блочными шифрами.

Хеши MD5 и SHA

Хеш создается вызовом функции CryptCreateHash , принимающей на входе контекст криптопровайдера, идентификатор алгоритма ( CALG_MD5 или CALG_SHA) и хендл ключа (для хешей с ключем типа MAC и HMAC). После этого хеш можно вычислять как явно, используя функцию CryptHashData , так и неявно, передавая хэндл хеша в функцию CryptEncrypt . Использование CryptEncrypt обсуждалось в разделе про DES, поэтому остановимся на функции CryptHashData . Ее вызов может выглядеть следующим образом:

Вычисление хеша (также как и шифрование) можно производить по частям (например, читая данные в буфер и хешируя содержимое буфера в цикле). После окончания хеширования можно либо подписать хеш (см. ниже), либо получить его значение вызовом:

Размер хеша MD5 равен 128 бит или 16 байтов. Для SHA это 160 бит или 20 байтов. После получения значения хеш использовать уже нельзя. Его нужно разрушить вызовом CryptDestroyHash . Проверка хеша производится точно также, как и его создание – нужно вычислить хеш и сверить полученное значение с сохраненным:

Алгоритмы цифровой подписи RSA и DSS

Позволяют установить корректность данных (точнее, их хеша) и принадлежность подписи владельцу закрытого ключа. Функции CryptoAPI позволяют подписывать только хеши, но не сами данные. Хеш предварительно должен быть вычислен (см. предыдущую главу), а затем подписан с помощью функции CryptSignHash на закрытом ключе для подписи (AT_KEYEXCHANGE) находящейся в контейнере криптопровайдера! Проверка подписи осуществляется на открытом ключе, который нужно предварительно импортировать:

Практический пример применения подписи можно увидеть в демонстрационном проекте encfile. Вычисление/проверка подписи RSA и DSS выполняется одинаково, однако для работы с подписью DSS нужно использовать Microsoft DSS Cryptographic Provider ( MS_DEF_DSS_PROV ,тип PROV_DSS ). Кроме того, подпись DSS работает только с алгоритмом хеширования SHA.

Криптографические классы в .NET

Большая часть криптографических классов (не абстрактных) .NET базируется на криптопровайдерах CryptoAPI. Однако имеются и исключения (например, SHA256Managed). В принципе, иерархия классов .NET позволяет абстрагироваться от конкретной реализации алгоритма, что может обеспечить простой переход на не привязанные к CryptoAPI классы в будущем. К сожалению, документация .NET по криптографическим классам, мягко говоря, оставляет желать лучшего. Поэтому часть из написанного далее следует воспринимать как собственные мысли и догадки автора (иногда полученные в ходе активного наступания на грабли).

Симметричные шифры DES, 3DES, Rijndael

Симметричные блочные шифры представлены в .NET классами DESCryptoServiceProvider , TripleDESCryptoServiceProvider , RijndaelManaged . Все эти классы являются потомками абстрактного класса SymmetricAlgorithm, описывающего все семейство блочных алгоритмов с симметричными ключами. Класс описывает свойства, позволяющие манипулировать основными параметрами алгоритма: размером блока, режимом работы, инициализационным вектором, ключом и другими. И методы CreateEncryptor и CreateDecryptor , возвращающие контексты (интерфейс ICryptoTransform ) для криптографических трансформаций данных. Также имеются методы GenerateKey и GenerateIV для генерации ключей и инициализационных векторов. Конкретные реализации наследуются от этого класса (возможно, через другие абстрактные классы – например, DESCryptoServiceProvider наследуется от класса DES , унаследованного от SymmetricAlgorithm ). Использование симметричных шифров демонстрируется в примере symmetric. Собственно шифрование выполняется следующим кодом:

Как можно видеть, ничего сложного в процессе шифрования/дешифрования нет. Использование базового класса SymmetricAlgorithm позволяет свести всю привязку к конкретному алгоритму к одной строчке – созданию экземпляра класса нужного алгоритма.

Стоит, однако, обратить внимание на необходимость явного задания инициализационного вектора ( IV ), поскольку, в отличие от CryptoAPI, он не инициализируется нулем по умолчанию, а выбирается случайно.

На вторые возможные грабли я наступил в коде дешифрования. Размер расшифрованных данных для не финального блока может быть меньше размера шифротекста. Ни с чем подобным в CryptoAPI я не сталкивался.

Наряду с самостоятельным чтением файла и шифрованием его по блокам, возможен еще вариант с использованием стрима CryptoStream . При этом вы создаете CryptoStream , передавая ему на входе стрим для записи и интерфейс трансформации, а затем просто пишете в этот стрим свои данные. Этот вариант описан в документации по .NET Framework, поэтому я не буду подробно на нем останавливаться.

Асимметричные шифры и цифровая подпись: RSA и DSA



Аналогично симметричным шифрам, все классы асимметричных шифров в .NET унаследованы от абстрактного базового класса AsymmetricAlgorithm . Конкретная реализация представлена классами RSACryptoServiceProvider и DSACryptoServiceProvider . Увы, здесь базовый класс уже не обеспечивает стандартных механизмов шифрования/дешифрования и ЭЦП, так что приходится иметь дело с конкретной реализацией. И тут дело опять не обошлось без сюрпризов. Первым сюрпризом является то, что нет стандартного способа сохранить приватный ключ RSA в файл. Мне пришлось самому организовывать шифрование ключа по паролю. Вторым, гораздо более неприятным, сюрпризом оказывается факт, что

Заявленные в классах RSACryptoServiceProvider и DSACryptoServiceProvider методы SignHash не работают. Вообще! При попытке передать в них хеш, сформированный с помощью MD5CryptoServiceProvider или SHA1CryptoServiceProvider , всегда вываливается исключение.

Поэтому приходится использовать классы RSAPKCS1SignatureFormatter и DSASignatureFormatter .

Третий (но не последний) веселый сюрприз – в описании метода Encrypt класса RSACryptoServiceProvider можно найти упоминания о максимальном размере куска данных, который можно зашифровать. Там утверждается, что для Win2k с установленным 128-битным шифрованием этот размер равен /8-11 байтов.

ПРЕДУПРЕЖДЕНИЕ

Не верьте этому! При попытке подсунуть блок данных длиной в 117 байт при длине ключа 1024 бита вы получите исключение. У меня получилось, что максимальный размер блока данных при таком размере ключа равен 87 байтам, т.е. на 30 байт меньше заявленного.

Последние два сюрприза чуть не повергли меня в состояние глубокой депрессии, т.к. я уже почти решил, что класс RSACryptoServiceProvider неработоспособен в принципе. Вот эти два перла из коллекции ошибок класса RSACryptoServiceProvider .

Метод ImportParameters , принимающий на входе структуру RSAParameters , содержащую публичный или приватный ключ, модифицирует эту структуру, хотя по нормальной человеческой логике и по документации он не имеет права этого делать.

Метод Decrypt , обратный методу Encrypt , не может расшифровать зашифрованный блок данных, если его первоначальный размер был больше 86 байтов (для 1024-битных ключей). Т.е. вы можете зашифровать блок из 87 байтов, но потом вы не сможете его расшифровать.

В общем, по моему нескромному мнению, классу RSACryptoServiceProvider нужно присудить заслуженную премию, как самому кривому из всех классов .NET. Тем не менее, хорошо то, что хорошо кончается. Я все-таки сумел найти и обойти все эти ошибки, и на свет все-таки родился пример (см. Asymmetric.zip), демонстрирующий прямое шифрование RSA, цифровую подпись RSA и DSA и хеширование в .NET.

Чтобы закончить рассмотрение асимметричных шифров в .NET, рассмотрим куски кода, отвечающие за генерацию ключей, шифрование, дешифрацию и цифровую подпись:

В качестве еще одного замечания стоит упомянуть, что хранить закрытые ключи RSA вне контейнеров CryptoAPI, пусть даже и временно, плохо. Их может перехватить злоумышленник. Также не стоит брать пример с моего кода сериализации закрытых ключей в файл – он был написан только для демонстрации, и сохраняет в файл много ненужной информации о типах данных, которая, теоретически, может упростить задачу дешифрование файла с ключом для злоумышленника.

Обмен симметричными ключами

За обмен сессионными ключами в .NET отвечают классы RSAOAEPKeyExchangeFormatter/Deformatter и RSAPKCS1KeyExchangeFormatter/Deformatter . Они унаследованы от базовых классов AsymmetricKeyExchangeFormatter/Deformatter , предоставляющих методы CreateKeyExchange и DecryptKeyExchange для шифрования и дешифрации сессионных ключей, соответственно. Рассмотрим передачу сессионного ключа на небольшом примере:

Как можно видеть, здесь нет ничего сложного. Самым проблемным, пожалуй, является передача публичного ключа от получателя отправителю и зашифрованного сессионного ключа в обратную сторону.

Все классы хешей MD5CryptoServiceProvider , SHA1CryptoServiceProvider , SHA1Managed , и т. д. в .NET унаследованы от базового класса HashAlgorithm , предоставляющего два различных пути для вычисления хеша – метод ComputeHash , вычисляющий хеш блока данных или потока за один раз, и методы TransformBlock и TransformFinalBlock , позволяющие вычислить хеш, разбивая данные на части. Вычисление хеша при помощи ComputeHash выглядит следующим образом:

Вариант с TransformBlock хорошо подходит для хеширования с параллельным шифрованием. Пример такого хеширования можно найти в примере Asymmetric. Вычисление хеша этим способом производится в цикле, причем на последней итерации нужно вызвать TransformFinalBlock :

Можно считать хеш целиком внутри цикла, а TransformFinalBlock вызвать для массива нулевой длины.

Цифровая подпись

Ввиду неработоспособности метода SignHash классов RSACryptoServiceProvider и DSACryptoServiceProvider, пары классов RSAPKCS1SignatureFormatter/Deformatter и DSASignatureFormatter/Deformatter , похоже, являются единственным вариантом создания цифровой подписи. Обе пары классов унаследованы от классов AsymmetricSignatureFormatter/Deformatter , предоставляющих стандартный интерфейс создания и верификации цифровой подписи – методы CreateSignature и VerifySignature . Перед вычислением или проверкой цифровой подписи нужно обязательно установить алгоритм хеширования, который будет использоваться в процессе работы, с помощью вызова SetHashAlgorithm . RSAPKCS1SignatureFormatter понимает два алгоритма хеширования – MD5 и SHA1, а DSASignatureFormatter — только SHA1. Пример создания и проверки подписи уже был приведен в разделе, посвященном асимметричным шифрам в .NET, и повторять его здесь я не буду.

Заключение

И CryptoAPI, и .NET предоставляют богатый набор средств шифрования, позволяющих организовать собственную систему защиты данных без использования сторонних средств. В .NET, правда, эти средства пока плохо отлажены и документированы, но использовать их все равно можно. Основными препятствиями на пути использования средств CryptoAPI и .NET могут стать отсутствие на платформах ниже Windows 2000 поддержки “сильной” криптографии и требования, налагаемые Российским законодательством на средства шифрования, используемые в коммерческой деятельности. По Российскому законодательству эти средства обязательно должны быть сертифицированы ФАПСИ. Но ни один из алгоритмов, реализованных в CryptoAPI и .NET, не является сертифицированным. Правда, это не мешает разработчикам shareware-программ использовать алгоритмы типа RSA для защиты своих продуктов.

Как вы используете bcrypt для хеширования паролей в PHP?

Время от времени я слышу совет «Использовать bcrypt для хранения паролей в PHP, правила bcrypt».

Но что такое bcrypt ? PHP не предлагает таких функций, Wikipedia болтает о утилите шифрования файлов и веб-поиске, просто раскрывает несколько реализаций Blowfish на разных языках. Теперь Blowfish также доступен на PHP через mcrypt , но как это помогает при хранении паролей? Blowfish – это шифр общего назначения, он работает двумя способами. Если он может быть зашифрован, его можно расшифровать. Пароли нуждаются в односторонней хэш-функции.

bcrypt – это алгоритм хэширования, который масштабируется с помощью аппаратного обеспечения (через настраиваемое количество раундов). Его медленность и несколько раундов гарантирует, что злоумышленник должен развернуть огромные средства и оборудование, чтобы взломать ваши пароли. Добавьте к этим солям по паролю ( bcrypt REQUIRES salt), и вы можете быть уверены, что атака практически невозможна без какой-либо смехотворной суммы средств или оборудования.

bcrypt использует алгоритм Eksblowfish для хэш-паролей. Хотя фаза шифрования Eksblowfish и Blowfish абсолютно одинакова, ключевая фаза расписания Eksblowfish гарантирует, что любое последующее состояние зависит как от соли, так и от ключа (пароль пользователя), и никакое состояние не может быть предварительно вычислено без знания обоим. Из-за этого ключевого отличия, bcrypt является односторонним алгоритмом хэширования. Вы не можете получить пароль обычного текста, не зная соли, раундов и ключа (пароль). [ Источник ]

Как использовать bcrypt:

Использование PHP> = 5.5-DEV


Теперь функции хеширования пароля теперь встроены непосредственно в PHP> = 5.5 . Теперь вы можете использовать password_hash() для создания хэша bcrypt любого пароля:

Чтобы проверить пароль, предоставленный пользователем для существующего хеша, вы можете использовать password_verify() как таковой:

Используя PHP> = 5.3.7, = 5.3.3)

В GitHub есть библиотека совместимости, основанная на исходном коде вышеуказанных функций, первоначально написанных на C, которая обеспечивает ту же функциональность. Как только библиотека совместимости установлена, использование будет таким же, как указано выше (за вычетом сокращенного обозначения массива, если вы все еще находитесь на ветке 5.3.x).

Использование PHP crypt() для генерации хэшей bcrypt входных строк. Этот класс может автоматически генерировать соли и проверять существующие хэши на вход. Если вы используете версию PHP выше или равную 5.3.7, настоятельно рекомендуется использовать встроенную функцию или библиотеку совместимости . Эта альтернатива предоставляется только в исторических целях.

Вы можете использовать этот код следующим образом:

Кроме того, вы также можете использовать Portable Hash Framework .

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

Причина проста: так сложно легко ввернуть bcrypt . Фактически, если вы посмотрите почти на каждый фрагмент кода на этой странице, вы заметите, что это нарушает хотя бы одну из этих общих проблем.

Face It, криптография сложна.

Оставьте это для экспертов. Оставьте это для людей, которые работают, чтобы поддерживать эти библиотеки. Если вам нужно принять решение, вы делаете это неправильно.

Вместо этого просто используйте библиотеку. Некоторые из них существуют в зависимости от ваших требований.

Библиотеки

Вот разбивка некоторых из наиболее распространенных API.

PHP 5.5 API – (доступно для 5.3.7+)

Начиная с PHP 5.5, вводится новый API для хеширования паролей. Существует также библиотека совместимости прокладок, которая поддерживается мной (5.3). Это имеет преимущество быть рецензируемой и простой в использовании реализации.

На самом деле, это было очень просто.

  • Документация: на PHP.net
  • Библиотека совместимости: на GitHub
  • PHP RFC: на wiki.php.net

Zend \ Crypt \ Password \ Bcrypt (5.3.2+)

Это еще один API, похожий на PHP 5.5, и делает аналогичную цель.

  • Документация: по Zend
  • Сообщение блога: Хеширование пароля с помощью Zend Crypt

PasswordLib

Это несколько иной подход к хешированию паролей. Вместо того, чтобы просто поддерживать bcrypt, PasswordLib поддерживает большое количество алгоритмов хеширования. Это в основном полезно в контексте, где вам необходимо поддерживать совместимость с устаревшими и разрозненными системами, которые могут быть вне вашего контроля. Он поддерживает большое количество алгоритмов хеширования. И поддерживается поддержка 5.3.2+

  • Исходный код / ​​документация: GitHub

PHPASS


Это слой, который поддерживает bcrypt, но также поддерживает довольно сильный алгоритм, который полезен, если у вас нет доступа к PHP> = 5.3.2 … Он фактически поддерживает PHP 3.0+ (хотя и не с bcrypt).

  • Код: cvsweb
  • Сайт проекта: на OpenWall
  • Обзор алгоритма md5 на bcrypt

Заворачивать

Есть много разных вариантов. Который вы выбираете, зависит от вас. Однако я бы настоятельно рекомендовал вам использовать одну из вышеупомянутых библиотек для обработки этого для вас.

Опять же, если вы используете crypt() напрямую, вы, вероятно, делаете что-то неправильно. Если ваш код использует hash() (или md5() или sha1() ), вы почти наверняка делаете что-то неправильно.

Просто используйте библиотеку …

Вы получите много информации в Enough With The Rainbow Tables: что вам нужно знать о безопасных схемах паролей или переносимой хеширования PHP .

Цель состоит в том, чтобы хешировать пароль с чем-то медленным, поэтому кто-то, получающий вашу базу данных паролей, умрет, пытаясь переборщить его (задержка в 10 мс для проверки пароля ничего для вас, много для кого-то, пытающегося переборщить ее). Bcrypt медленный и может использоваться с параметром, чтобы выбрать, насколько он медленный.

Вы можете создать односторонний хеш с помощью bcrypt с помощью функции crypt() PHP и передать соответствующую соль Blowfish. Наиболее важным из всего уравнения является то, что A) алгоритм не был скомпрометирован и B) вы должным образом соляли каждый пароль . Не используйте соль для всей заявки; который открывает все ваше приложение для атаки из одного набора таблиц Rainbow.

PHP – функция склепа

Изменить: 2013.01.15. Если ваш сервер будет поддерживать его, используйте вместо этого решение martinstoeckli .

Все хотят сделать это более сложным, чем оно есть. Функция crypt () выполняет большую часть работы.

Я знаю, что это должно быть очевидно, но, пожалуйста, не используйте «пароль» в качестве пароля.

Версия 5.5 PHP будет иметь встроенную поддержку BCrypt, функции password_hash() и password_verify() . На самом деле это всего лишь обертки вокруг функции crypt() и облегчат ее правильное использование. Он заботится о создании безопасной случайной соли и обеспечивает хорошие значения по умолчанию.

Самый простой способ использовать эти функции:

Этот код будет хэш-пароль с помощью BCrypt (алгоритм 2y ), генерирует случайную соль из случайного источника ОС и использует параметр стоимости по умолчанию (на данный момент это 10). Вторая строка проверяет, совпадает ли введенный пользователем пароль с уже сохраненным хэш-значением.

Если вы хотите изменить параметр стоимости, вы можете сделать это так, увеличивая параметр стоимости на 1, удваивая необходимое время для вычисления значения хэш-функции:

В отличие от параметра «cost» , лучше опустить параметр «salt» , потому что функция уже делает все возможное, чтобы создать криптографически безопасную соль.

Для PHP версии 5.3.7 и более поздних версий существует пакет совместимости от того же автора, который сделал функцию password_hash() . Для версий PHP до 5.3.7 нет поддержки для crypt() с 2y , безопасным алгоритмом BCrypt для unicode. Вместо него можно заменить 2a , что является лучшей альтернативой для ранних версий PHP.

Альтернативой является использование scrypt, специально разработанного для того, чтобы превосходить Bcrypt Колина Персиваля в его статье . В PECL есть расширение PHP с расширением scrypt . В идеале этот алгоритм будет переведен в PHP, чтобы его можно было указать для функций password_ * (в идеале, как «PASSWORD_SCRYPT»), но этого пока нет.

Текущее мышление: хеши должны быть самыми медленными, а не самыми быстрыми. Это подавляет атаки радужного стола .

Также связано, но предосторожно: злоумышленник никогда не должен иметь неограниченный доступ к вашему экрану входа в систему. Чтобы предотвратить это: настройте таблицу отслеживания IP-адреса, которая записывает каждое нажатие вместе с URI. Если более 5 попыток входа в систему поступают с одного и того же IP-адреса в любой пятиминутный период, заблокируйте с объяснением. Вторичный подход состоит в том, чтобы иметь двухуровневую схему паролей, например, банки. Помещение блокировки для сбоев на втором проходе повышает безопасность.

Резюме: замедлить атакующего, используя длительные хэш-функции. Кроме того, заблокируйте слишком много обращений к вашему логину и добавьте второй уровень пароля.

Необратимое шифрование с уникальным результатом

Подскажите, как решить вот такую задачку. Есть id пользователя, типа 1234567890, нужно зашифровать его так, чтобы его нельзя было расшифровать обратно, но он остался уникальным. То есть мне нужна возможность посчитать уникальных пользователей, но не иметь возможности узнать их настоящие id.

Я склоняюсь к md5(id).str(0,24) ;

1 ответ 1

Решается задача обычно так:


  1. Генерируем т.н. соль/salt — случайный набор символов, соль обычно одна и та же для всех наборов.
  2. Проводим конкатенацию id юзера и соли.
  3. Вычисляем хэш (берем более-менее любой алгоритм), пусть это будет MD5, хотя я бы рекомендовал SHA-256 или Whirlpool — MD5 уже давно считается слабым хэш алгоритмом.

Итоговый алгоритм будет примерно такой: hash(id+salt) , некоторые извращенцы любят измываться и так: hash(hash(id+salt1)+salt2) — ну и т.д. — насколько позволит фантазия.

В таком разрезе вероятность взлома хэша простым перебором или радужными таблицами резко (на порядки) снижается.

Многозначное шифрование с использованием хеш-функций

В последнее время приходится все больше задумываться о сохранности анонимности и безопасности относительно прав на информационную собственность. В этой заметке я предложу довольно интересное решение относительно шифрования, позволяющего сохранить несколько различных объектов в одном контейнере с разными мастер-ключами, и гарантирующее отсутствие «следов» других сущностей при получении какой-либо одной. Более того, в силу конструктивных особенностей алгоритма — даже наличие расшифрованной сущности можно всегда списать на «случайность» (то есть, нет никаких средств проверить, были ли изначально зашифрованы эти данные или нет). Кроме того, алгоритм имеет чрезвычайную стойкость к атакам «подбора ключа». Правда у метода есть и существенный недостаток — катастрофически низкая скорость работы, но в ряде особенных случаев он все равно может быть полезен.

Ядро метода — математический объект под названием хеш-функция. В качестве небольшого ликбеза рекомендую изучить также и мою предыдущую заметку.

Существует ряд довольно занятных заблуждений о хеш-функциях:

  • Хеш-функцию легко обратить. Якобы существует некая программа md5decrypt, которая восстанавливает результат md5crypt;
  • Хеш-функции вроде MD5 уже давно сломаны, т.к. получить коллизию на заданную строку — плевое дело;
  • Хеш-функции являются алгоритмом шифрования.

Все это — очевидно ерунда. Криптографические хеш-функции по определению сложны для обращения, и для используемых ныне методов (MD5, SHA) никто обратного не доказал. Коллизии мы научились строить только в самом общем виде, генерируя разные строки s1, s2, такие что f(s1) = f(s2). А шифрованием хеширование не является, хотя бы потому, что отсутствует понятие ключей шифрования.

Но иногда «ерунда» может помочь сконструировать интересное решение, попробуем на секунду представить, что вышеизложенные тезисы справедливы. Даже не так, построим объект, для которого они будут выполняться.

Пусть R(key, message) = первые N бит md5(key + message), где N — не очень велико, скажем равно 40. И тут начинается интересное!

Во-первых, при заданном ключе key, мы без труда можем строить большое количество разных сообщений, дающих нужное значение функции. Перебрать порядка 2^40 различных строк — задача посильная обычному «домашнему» компьютеру. Если мы имеем заданное значение X, которое является результатом применения функции R мы без труда найдем и прообраз.

То есть, пусть X = R(‘test_key’, ‘msg’) = 5B7AF38712
Забываем про сообщение ‘msg’, имеем только хеш-значение 5B7AF38712 и исходный ключ ‘test_key’, запускаем перебор, и через некоторое время получаем строку ‘msg’ в качестве прообраза!

Но что, если мы не знаем и ключа (‘test_key’) а знаем только хеш-значение? Тогда мы можем построить бесконечное количество пар ключ-сообщение, дающие наше значение X.

Некоторые из этих пар даже будут выглядеть осмысленно, что бы это ни значило. Более того, одной из таких пар будет главный вопрос жизни вселенной и всего такого (в некоторой формулировке) и ответ на него.

Почему так? Просто потому, что множество входных значений (то есть ключей и сообщений) бесконечно, а функция R обладает конечным количеством возможных значений, каждое из которых (очень грубо говоря) имеет равные шансы на появление.

Теперь, думаю, идея процесса понятна — отправляя человеку ‘5B7AF38712’, он, зная ключ (‘test_key’), быстрее всего получит сообщение (‘msg’) (но пока нет гарантий этого, нужно заметить). А, не зная, — любой мусор.

Магия

Теперь мы хотим научиться кодировать сообщения так, чтобы адресат смог расшифровать их правильно с определенными гарантиями. А еще, мы довольно хитры, и хотим чтобы та же посылка могла быть расшифрована с другим ключом как другое заданное сообщение.

То есть, на входе: key1, message1 и key2, message2.
Причем crypt(key1,message1,key2,message2) = X,
и decrypt(X,key1) = message1, а decrypt(X,key2) = message2.

Здесь нам придется «модернизировать» функцию R, мы построим целый набор функций:
Rm(key,msg) = первые N бит md5(key + строка(m) + msg),
где строка(m) — любое однозначное представление числа m как строки (например десятичная запись).

Индекс m — специальное «контрольное значение», которое мы будем варьировать при шифровании сообщений (то есть, оно выбирается нашим алгоритмом).

Прежде чем рассказать, как оно выбирается, я предложу процедуру расшифровки, благо она довольно проста:
decrypt(X,key) = значение msg, такое, что R(key,msg) == X, при наименьшем m.

В итеративной форме можно записать так (псевдокод):

Здесь появляется новый параметр MAX_STRING_LENGTH, отвечающий за максимальную длину строки, которую мы можем получить перебором. Его имеет смысл ограничивать адекватной производительностью перебора, например, если исходное сообщение было ‘hello, Habrahabr’, то непосредственно строки для шифрования придется разбить на более короткие последовательности, например ‘hell’, ‘o, H’, ‘abra’, ‘habr’ и к каждой из них примерить функцию encrypt, не забывая, про альтернацию ключа.

Коллизию для первых 40 бит получить несложно, и эта функция гарантированно вернет какое-либо значение. Задача функции «шифрования» обеспечить возврат нужного значения.

Но ведь это тоже можно сделать перебором:

Таким образом, мы обеспечиваем уверенность в верной расшифровке ее моделированием с различными параметрами. Если алгоритм расшифровки не меняется, то это гарантия корректности, и заметьте — никаких контрольных сумм, валидаторов, прочих вещей, позволяющих «выдать» факт правильной расшифровки!

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

Однако здесь есть есть некоторые подводные камни — при фиксированном диапазоне «контрольных значений» (m) — такой функции Rm может не существовать, и придется расширять диапазон. А расширение диапазона снижает скорость алгоритма. В общем случае диапазон для значения m должен быть больше, чем разрядность хеш-функции R, так что мы можем просто ее уменьшить (что я и сделал в тестовом прототипе). Но при сильно уменьшенном диапазоне выходных значений таких m тоже может просто не существовать.


В целом при реализации прототипа я остановился на двухбайтном усечении. Кроме того, для частичного решения вопроса производительности (я быстро набросал прототип на C#, а «местные» провайдеры криптографии просто ужасно медлительны! Практически в 50000 раз медленнее, чем реализация на GPU, которую я постараюсь выполнить чуть позже) я оставил возможность использовать только буквы диапазона a-z. В таком «печальном» виде это уже работает. Вводим две пары ключ-значение, жмем «Encrypt2» и получаем через некоторое время хеш-код. С первым ключом он быстро расшифровывается в первое сообщение:

Со вторым, соответственно, во второе:

Заметьте, все тот же encrypted text. Ну а с неправильным ключом — в странное сообщение (но где гарантии, что не оно было исходным?):

В нем довольно много нелепых ограничений, но для возмущенных, напомню, что это просто иллюстрация идеи. А над хорошим приложением при условии актуальности идеи можно будет и подумать.

Шифрование и хэширование пакета

Пакет — неделимый фрагмент данных, подаваемых на функции шифрования CPEncrypt(), CPCEncrypt(),CPDecrypt(), CPCDecrypt(). Пакет состоит из полей:

Поле Описание
Предзаголовок пакета (DIVERSBLOB) Опциональное поле, содержащее блоб диверсификации ключа шифрования и хэширования по алгоритму CALG_PRO_DIVERS. Признак обработки поля — установленный флаг CP_CHP_STARTMIX.
Заголовок пакета (HEADER) Опциональное поле длины не более 255 байт. Не шифруется, хэшируется хэш-функцией hHash.
Вектор инициализации (IV) Опциональное поле, содержащее вектор инициализации шифрования пакета. Не шифруется, хэшируется опционально.
Тело пакета (PAYLOAD) Обязательное поле, шифруется и хэшируется.
Трейлер (TRAILER) Опциональное поле длины не более 254 байт. Не шифруется, хэшируется хэш-функцией hHash.
Знечение функции хэширования пакета (HASH) Опциональное поле, может быть зашифровано.

При шифровании пакетов поддерживаются режимы шифрования: CRYPT_MODE_CNT, CRYPT_MODE_CFB, CRYPT_MODE_CBCSTRICT. Во всех режимах шифрования могут быть использованы флаги: CRYPT_PROMIX_MODE, CRYPT_SIMPLEMIX_MODE.

В пакетном режиме шифрования длина пакетов остаётся неизменной.

В режиме CBC общая длина шифруемых данных должна быть кратна 8, также должна быть кратной 8 длина каждого шифруемого элемента IOVEC, в противном случае возвращается ошибка NTE_BAD_DATA. Пакеты обрабатываются с сохранением размера полей, паддинг в режиме CBC игнорируется.

Флаги CP_CHP_IV_RANDOM, CP_CHP_IV_USER, CP_CHP_HASH_PACKET предназначены для обработки пакетов в потоках с возможным нарушением порядка следования пакетов, с возможной потерей пакетов.

Флаги CP_CHP_IV_CHAIN, CP_CHP_HASH_CHAIN, CP_CHP_HASH_NONE предназначены для обработки пакетов в потоках, гарантирующих доставку всех пакетов в неизменной последовательности.

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

Значение флагов Условия и ограничения режима
CRYPT_SIMPLEMIX_MODE совместно с CP_CHP_IV_RANDOM, CRYPT_SIMPLEMIX_MODE совместно с CP_CHP_IV_USER Обработка пакетов может выполняться с установленным флагом Final=FALSE. Объём данных, обрабатываемых на одном ключе, ограничен 4М.
CRYPT_PROMIX_MODE совместно с CP_CHP_IV_RANDOM, CRYPT_PROMIX_MODE совместно с CP_CHP_IV_USER Обработка пакетов должна выполняться с установленным флагом Final=TRUE. Объём данных, обрабатываемых на одном ключе, ограничен 4М либо числом пакетов = 0.

Структура пакета и порядок обработки полей пакета определяются значениями флагов параметра dwFlags, объединяемыми операцией OR; значения флагов зависимы, не все сочетания значений полей допустимы. Для формирования флагов рекомендуется использовать макрос CP_CHP().

Crypt — Необратимое шифрование (хэширование)

Рассмотри основные алгоритмы шифрования, а также применение функции crypt для создания хэш паролей в php скриптах .

Функция crypt() — возвращает хэшированную строку, полученную с помощью стандартного алгоритма DES, MD5 или любого другого, имеющегося в системе. Следует отметить, что функция расшифровки отсутствует, так как crypt() использует необратимый алгоритм хэширования.

Общий формат функции crypt: string crypt ( string $str [, string $salt ] )

где $str — Хэшируемая строка; $salt — Необязательный параметр, определяет вид хэширования. Если соль не указана, будет автоматически сгенерирована стандартная случайная двухсимвольная (DES) либо двенадцатисимвольная (MD5) соль, в зависимости от доступности алгоритма MD5 в crypt().

Алгоритмы хэширования (шифрования)

CRYPT_STD_DES — Стандартное DES-шифрование с двухсимвольной солью из алфавита «./0-9A-Za-z»»./0-9A-Za-z».

CRYPT_EXT_DES — Расширенное DES-шифрование. «Соль» является девятисимвольной строкой, состоящей из символа подчеркивания, за которым следуют 4 байта счетчика итерации и 4 байта соли. Эти данные будут закодированы в виде печатаемых символов, 6 бит на символ, с меньшим значащим символом указанным первым. Значения от 0 до 63 кодируются как «./0-9A-Za-z».

CRYPT_MD5 — MD5-шифрование с 12-символьной солью, начинающейся с «$1$».

CRYPT_BLOWFISH — Blowfish-шифрование с 16-символьной salt, начинающейся с «$2$».

CRYPT_SHA256 — хэш SHA-256 с шестнадцатисимвольной солью, начинающейся с «$5$».

CRYPT_SHA512 — хэш SHA-512 с шестнадцатисимвольной солью, начинающейся с «$6$».

Скрипт php для создания хэша для htpasswd

При создании пароля для использования в файле .htpasswd Apache, нужно использовать 2 первых символа пароля в качестве salt. Для этого используем функцию substr, которая вернет два первых символа.


Вот и все. Теперь вы умеете генерить хэши для htpasswd Комментируем, подписываемся ну и всем пока:)

Шифрование с использованием crypt()

В настоящее время я использую очень безопасную систему входа в систему, но я новичок в функции crypt() и нуждаюсь в быстрой помощи.

Я использовал crypt() для шифрования строки пароля во время регистрации и сохранения ее в базе данных. Однако как я смогу расшифровать ключ во время входа? Или как я должен делать иначе? Или, возможно, можно было бы сделать магию с представленной строкой пароля, чтобы сравнить ее с зашифрованным ключом в базе данных?

crypt() не шифрует пароли, он их хэширует. Основное различие заключается в том, что вы не можете вернуть хэшированные пароли (подумайте о хэш-коричневых тонах — если у вас есть хеш-коричневые цвета, вы не сможете вернуть картофель).

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

Прочитайте документацию на crypt() , чтобы понять, что «волшебство» за кодом выше работает.

crypt() предоставленный пароль при входе в систему. Сравните вывод с предыдущим выходом crypt() . Если они совпадают, пароли соответствуют.

Это основная теория работы односторонней хэш-функции.

Не шифруйте пароль. Вместо этого сохраните его с помощью хэша.

И так вы должны это делать. Обратите внимание, что этот код — это то, как я это сделал, и вы можете изменить некоторые из вещей. И вы должны определить свою собственную уникальную соль, будь то в файле конфигурации или иначе. Он должен либо a) находиться в глобальном масштабе, как я опубликовал, либо вы можете изменить его, чтобы определить его в функции. Также вы не шифруете, вы на самом деле хешируете. Шифрование — оба способа, хеширование — это шифрование одним способом. Это означает, что вы не можете расшифровать хэш. Вы можете только грубо угадать оригинальный текст.

Как указано в коде, лицензия CC-By, поскольку я считаю, что это достаточно хорошо для большинства вещей. Кроме того, держите блок таким же, как ссылка на эту страницу, как и то, что я делаю со всем своим исходным кодом. Также я понимаю, что «случайная» строка на самом деле не такая случайная, но она достаточно случайна для использования вами с той целью, в которой она будет.

Изменить 2: Также избегайте имени пользователя. Я не избегаю пароля, так как я его хэширую, поэтому избежать этого не нужно, поскольку он уже смягчен и будет просто тратить циклы. Но только если вы делаете что-то подобное. Обязательно избегите имени пользователя mysql_real_escape_string. Если вы используете php5 +, вы должны изучить mysqli (если вы используете mysql). Если вы используете другую систему, вам придется искать ее самостоятельно, так как я знаю только mysql. Я уйду на пару дней, поэтому я действительно надеюсь, что это сработает для вас. Я проверю его время от времени, но я могу забыть. так что да. Надеюсь, это поможет вам, так как это безопасно, безопасно и должно хорошо работать для вас.

Изменить 3: Изменена функция случайных строк, чтобы сделать ее немного сильнее, так как я забыл, что это будет использовать для генерации временных паролей. Это должно сделать его достаточно случайным для использования для этой цели, поскольку иначе сгенерированный пароль может быть известен кем-то, зная точное время (с текущим микротрейдом), хотя это маловероятно, это все еще делает его немного сильнее и должно сделать это от таких видов атак. Он не должен быть полностью готовым к производству и должен быть защищен для вашей системы. Просто не забудьте установить переменную $unique_salt somwhere в глобальной области или установить ее каждый раз, когда она используется в каждой из этих функций.

crypt

crypt — одностороннее шифрование строк (хэширование).

Описание

string crypt (string str [, string salt])

crypt() возвратит шифрованную строку с использованием стандартного алгоритма шифрования Unix на базе DES или альтернативных алгоритмов, которые могут быть доступны в данной системе. Аргументами является шифруемая строка и необязательная строка salt как база шифрования. См. Unix man-страницу для вашей crypt-функции.

Если аргумент salt не предоставлен, он будет произвольно сгенерирован PHP.

Некоторые ОС поддерживают более одного типа шифрования. На практике иногда стандартное DES-шифрование заменяется алгоритмом на базе MD5. Тип шифрования переключается аргументом salt. Во время установки PHP определяет возможности crypt-функции и будет принимать salts для других типов шифрования. Если salt не предоставлен, PHP будет автоматически генерировать стандартный двухсимвольный salt по умолчанию, если только тип шифрования по умолчанию в системе не MD5 — тогда генерируется произвольный MD5-совместимый salt. PHP устанавливает константу CRYPT_SALT_LENGTH, которая сообщает вам, применяется ли для вашей системы обычный двухсимвольный salt или более длинный 12-символьный salt.

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

Стандартная шифровка crypt() на базе DES возвращает salt как первые два символа вывода. Она также использует лишь первые восемь символов из str, поэтому строки большей длины, начинающиеся теми же самыми восемью символами, сгенерируют тот же результат (когда используется тот же salt).

В системах, где функция crypt() поддерживает несколько типов шифрования, следующие константы устанавливаются в значения 0 или 1, в зависимости от того, доступен ли данный тип:

CRYPT_STD_DES — стандартное шифрование на базе DES с двухсимвольным salt

CRYPT_EXT_DES — расширенное шифрование на базе DES с девятисимвольным salt

CRYPT_MD5 — шифрование MD5 с 12-символьным salt, начиная с $1$

CRYPT_BLOWFISH — Blowfish-шифрование с 16-символьным salt, начиная с $2$

Примечание: отсутствует функция дешифрования, поскольку crypt() использует однопутный алгоритм.

Илон Маск рекомендует:  Как избавиться от 16 ти цветов в панели инструментов
Понравилась статья? Поделиться с друзьями:
Кодинг, CSS и SQL