Можете ли вы преобразовать вывод php crypt() в действительный MD5?

У меня есть несколько строк, зашифрованных с помощью функции PHP crypt().

Выводы выглядят примерно так:

$1$Vf/.4.1.$CgCo33ebiHVuFhpwS.kMI0
$1$84..vD4.$Ps1PdaLWRoaiWDKCfjLyV1
$1$or1.RY4.$v3xo04v1yfB7JxDj1sC/J/

Хотя я считаю, что crypt() использует алгоритм MD5, выходные данные не являются действительными хэшами MD5.

Есть ли способ преобразовать полученные хэши в действительные хэши MD5 (16-байтовые шестнадцатеричные значения)?


Обновление:

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

9e107d9d372bb6826bd81d3542a419d6  
e4d909c290d0fb1ca068ffaddf22cbd0  
d41d8cd98f00b204e9800998ecf8427e

(взято из Википедии)

Есть ли способ конвертировать хэши, которые у меня есть, в такие, как указано выше?


person TheKeys    schedule 20.01.2009    source источник
comment
Не могли бы вы немного пояснить, чего вы пытаетесь достичь? Без обид, но это звучит как шаг 1 в рецепте проведения атаки радужной таблицы на базу данных паролей, и люди могут не захотеть помочь, если они не убеждены, что это не для злых целей...   -  person Mihai Limbășan    schedule 20.01.2009
comment
Один результат закодирован в base64, другой — только в base16.   -  person Edouard A.    schedule 20.01.2009
comment
@genesis: Почему вы меняете URL-адрес, на который ссылаетесь, но не тот, который показан на странице?   -  person Paŭlo Ebermann    schedule 13.11.2011
comment
@PaloEbermann: потому что я забыл об этом   -  person Martin.    schedule 14.11.2011


Ответы (5)


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

$1$        # this indicates that it is MD5
Vf/.4.1.   # these eight characters are the significant portion of the salt
$          # this character is technically part of the salt, but it is ignored
CgCo33eb   # the last 22 characters are the actual hash
iHVuFhpw   # they are base64 encoded (to be printable) using crypt's alphabet
S.kMI0     # floor(22 * 6 / 8) = 16 (the length in bytes of a raw MD5 hash)

Насколько мне известно, алфавит, используемый криптой, выглядит так:

./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz

Итак, учитывая все это, вот как вы можете преобразовать 22-символьный хэш crypt-base64 в 32-символьный хэш base16 (шестнадцатеричный):

Во-первых, вам нужно что-то для преобразования base64 (с пользовательским алфавитом) в необработанный 16-байтовый хэш MD5.

define('CRYPT_ALPHA','./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz');
/**
 * Decodes a base64 string based on the alphabet set in constant CRYPT_ALPHA
 * Uses string functions rather than binary transformations, because said
 * transformations aren't really much faster in PHP
 * @params string $str  The string to decode
 * @return string       The raw output, which may include unprintable characters
 */
function base64_decode_ex($str) {
    // set up the array to feed numerical data using characters as keys
    $alpha = array_flip(str_split(CRYPT_ALPHA));
    // split the input into single-character (6 bit) chunks
    $bitArray = str_split($str);
    $decodedStr = '';
    foreach ($bitArray as &$bits) {
        if ($bits == '$') { // $ indicates the end of the string, to stop processing here
            break;
        }
        if (!isset($alpha[$bits])) { // if we encounter a character not in the alphabet
            return false;            // then break execution, the string is invalid
        }
        // decbin will only return significant digits, so use sprintf to pad to 6 bits
        $decodedStr .= sprintf('%06s', decbin($alpha[$bits]));
    }
    // there can be up to 6 unused bits at the end of a string, so discard them
    $decodedStr = substr($decodedStr, 0, strlen($decodedStr) - (strlen($decodedStr) % 8));
    $byteArray = str_split($decodedStr, 8);
    foreach ($byteArray as &$byte) {
        $byte = chr(bindec($byte));
    }
    return join($byteArray);
}

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

/**
 * Takes an input in base 256 and encodes it to base 16 using the Hex alphabet
 * This function will not be commented.  For more info:
 * @see http://php.net/str-split
 * @see http://php.net/sprintf
 *
 * @param string $str   The value to convert
 * @return string       The base 16 rendering
 */
function base16_encode($str) {
    $byteArray = str_split($str);
    foreach ($byteArray as &$byte) {
        $byte = sprintf('%02x', ord($byte));
    }
    return join($byteArray);
}

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

/**
 * Takes a 22 byte crypt-base-64 hash and converts it to base 16
 * If the input is longer than 22 chars (e.g., the entire output of crypt()),
 * then this function will strip all but the last 22.  Fails if under 22 chars
 *
 * @param string $hash  The hash to convert
 * @param string        The equivalent base16 hash (therefore a number)
 */
function md5_b64tob16($hash) {
    if (strlen($hash) < 22) {
        return false;
    }
    if (strlen($hash) > 22) {
        $hash = substr($hash,-22);
    }
    return base16_encode(base64_decode_ex($hash));
}

Учитывая эти функции, представление base16 ваших трех примеров:

3ac3b4145aa7b9387a46dd7c780c1850
6f80dba665e27749ae88f58eaef5fe84
ec5f74086ec3fab34957d3ef0f838154

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

person Dereleased    schedule 10.02.2010
comment
Я боюсь, что вывод md5-crypt отличается от хэша md5 не только кодировкой base64 с пользовательским алфавитом. Глядя на один из примеров реализации, google.com/codesearch/p?hl=en#eiS4vny31P0/Linux-PAM-0.99.7.0/ md5-crypt также обрабатывает хэши таким образом, чтобы сделать шифрование ресурсоемким. (относительно вычислительной мощности, доступной в то время, см. комментарий о 60-мегагерцовом Pentium). Предлагаемое вами решение не инвертирует это преобразование. - person Francois G; 17.12.2010
comment
Черт возьми, вернулся из могилы, чтобы поставить мне минус, да еще и не на основе вопроса? Проблема заключалась в том, что значения, выходящие из функции crypt(), ВЫГЛЯДИЛИ НЕ так, как хотел задающий вопрос — это было решение проблемы задающего вопроса с визуальным представлением хэша, а не создание хэша, созданного md5(), справедливым. к хешу, созданному crypt(). По моему скромному мнению, кнопка минусовать не для этого. - person Dereleased; 21.12.2010
comment
Версии с кодировкой base64 не имеют заполнения. Хэши имеют длину 128 бит, поэтому после прохождения по 6 бит за раз у вас останется 2 бита. Очевидное решение состоит в том, чтобы использовать их как LSB и использовать индекс как обычно. Странно то, что хотя каждый из предоставленных хэшей заканчивается битовыми парами 00, последний символ их представления base64 (от Q) отличается. Не могли бы вы объяснить это? - person buherator; 04.08.2013
comment
@buherator, (1/2) извините, что не заметил этого раньше. Во-первых, 22 символа x 6 бит = 132 бита данных. Мы могли бы взять дополнительные 4 бита из 132 Mod 8 и создать еще один байт, но это вряд ли будет правильным решением, потому что (а) это означает, что мы могли бы когда-либо генерировать вывод только для последнего байта в очень определенном диапазоне (старшем или старшем). низкий порядок), что кажется маловероятным, и (b) длина хеша MD5 известна как 128 бит. Кажется, это указывает на то, что оставшиеся 4 бита можно отбросить, но этот путь привел меня к другому, в следующем комментарии. - person Dereleased; 22.09.2015
comment
@buherator, (2/2). Проанализировав примеры хэшей, я заметил, что всегда оставалось немного данных, которые я усекал в последних четырех битах; на самом деле казалось, что последний символ всегда был относительно небольшим числом, которое содержало бы данные только в последних двух битах. Скорее всего, это означает одну из двух вещей (возможно, обе): (а) Мой пользовательский алфавит B64 неверен. (b) Значения сериализуются в обратном порядке. Тестирование делает второй случай более вероятным, а использование strrev рядом с sprintf в base64_decode_ex исправляет его. - person Dereleased; 22.09.2015

$1$ действительно означает, что это хэш MD5, но crypt генерирует случайную соль. Вот почему вы найдете другое значение MD5. Если вы включите сгенерированную соль, вы получите тот же результат.

Соль закодирована в base64 на выходе как хэш.

Используемый алгоритм является общесистемным параметром. Вообще это MD5, вы правы.

person Edouard A.    schedule 20.01.2009

Я считаю, что ответ на мой первоначальный вопрос - нет, вы не можете конвертировать из одного формата в другой.

Хеши, сгенерированные php crypt(), по-видимому, генерируются версией реализации хеширования FreeBSD MD5, созданной Полом-Хеннингом Кампом.

http://people.freebsd.org/~phk/

person TheKeys    schedule 21.01.2009

Судя по документации, это зависит от системы. Вы можете принудительно использовать используемый алгоритм, установив параметр соли. Из документов:

Тип шифрования запускается аргументом соли. Во время установки PHP определяет возможности функции шифрования и принимает соли для других типов шифрования. Если соль не указана, PHP автоматически сгенерирует стандартную двухсимвольную соль по умолчанию, если тип шифрования по умолчанию в системе не MD5, и в этом случае генерируется случайная соль, совместимая с MD5.

person Ray Booysen    schedule 20.01.2009

С http://php.net/crypt:

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

Вам нужна функция md5():

Вычисляет хэш MD5 строки str, используя алгоритм » RSA Data Security, Inc. MD5 Message-Digest, и возвращает этот хэш. длина 16. По умолчанию FALSE.

person Sven Lilienthal    schedule 20.01.2009
comment
На самом деле, один из альтернативных алгоритмов (обозначенный $1$) представляет собой (соленый) алгоритм MD5. - person Paŭlo Ebermann; 13.11.2011