Различия между Crypt.crypt() и DigestUtils.md5() в apache.commons.Codec

Я пишу базовый взломщик паролей для схемы хеширования MD5 против Linux /etc/shadow file, Когда я использую commons.codec"s DigestUtils или же Crypt библиотеки, длина хеша для них разные (между прочим).

Когда я использую Crypt.crypt(passwordToHash, "$1$Jhe937$") вывод представляет собой строку из 22 символов. Когда я использую DigestUtils.md5[Hex](passwordToHash + "Jhe937")(или Java MessageDigest класс) вывод представляет собой 32-символьную строку (после преобразования). Это не имеет смысла для меня.


в сторону: нет ли простого способа преобразовать DigestUtils.md5(passwordToHash)"s byte[] в строку. Я перепробовал все * способы, и я получаю все недопустимые выходные данные: Nz_èJÓ_µù[î¬y

* все это: `new String(byte[], "UTF-8") и конвертировать в char, а затем в String

1 ответ

Решение

Суть в том, что хотя они будут выполнять одинаковое хеширование, формат вывода между ними будет разным, поэтому длины будут разными. Читайте дальше для деталей.

MD5 - это алгоритм переваривания сообщений, который выдает 16-байтовое хеш-значение, всегда (при условии правильного ввода и т. Д.). Эти байты не все печатаемые символы, они могут принимать любое значение от 0 до 255 для любого байта, в то время как для печати символы в ASCII находятся в диапазоне 32-126.

DigestUtils.md5 (String) генерирует MD5 строки и возвращает 16-байтовый массив элементов. DigestUtils.md5Hex(String) - это удобная оболочка (я полагаю, я не смотрел на источник, но я бы так написал:-)) вокруг DigestUtils.md5, который принимает 16-элементный байтовый массив, который производит md5 и base16 кодирует его (также известный как шестнадцатеричное кодирование). Это заменяет каждый байт эквивалентными двумя шестнадцатеричными символами, поэтому вы получаете из него 32-символьную строку.

Crypt.crypt использует специальный формат, который восходит к первоначальному методу хранения паролей Unix. За прошедшие годы он был расширен за счет использования различных алгоритмов хеширования / шифрования, более длинных солей и дополнительных функций. Он также кодирует свой вывод в печатный текст, из которого исходит разница в длине. Используя соль "$1$...", вы говорите, что используете MD5, поэтому пароль и соль будут хэшироваться с использованием MD5, что приведет к 16 байтам, как и ожидалось, но, поскольку эти байты не обязательно для печати, хеш кодируется в base64 (используя немного другой алфавит, чем стандартная кодировка base64), который заменяет 3 байта на 4 печатных символа. Таким образом, 16 байтов становятся 16 / 3 * 4 = 21-1/3 символа, округленные до 22.

Кроме того, DigestUtils.md5 выдает 16 байтов, но эти байты могут иметь любое значение от 0 до 255 и (эффективно) являются случайными. new String(byte[], "UTF-8") говорит, что байты в массиве байтов являются кодировкой UTF-8, которая является очень специфическим форматом. new String делает все возможное, чтобы обрабатывать байты как строку в кодировке UTF-8, но поскольку на самом деле это не так, вы, как правило, получаете бред. Если вы хотите что-то для печати, вам придется использовать что-то, что принимает случайные байты, а не байты в определенном формате (например, UTF-8). Два популярных варианта - это кодирование base16/hex, которое вы можете получить с помощью DigestUtils.md5Hex, или base64, которое вы можете получить с помощью Base64.encodeBase64String(DigestUtils.md5(pwd + salt)).

Другие вопросы по тегам