Как хэшировать несколько раз и объединять строку в каждом раунде

Я пишу программу, которая объединяет слово R в конце пароля, а затем вычисляет хэш SHA-256. Позже снова добавьте слово R в конец шестнадцатеричного результата и рассчитайте новый хэш с помощью SHA256.

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

Что-то вроде этого в псевдокоде:

hash = SHA256(...(SHA256(SHA256(“password”||R)||R)||R)..)

В настоящее время я тестирую свой код, хэшируя 2 раза:

   String R = "f@ghj!$g";
   hash = password.concat(R);

   MessageDigest md = MessageDigest.getInstance("SHA-256");
   digest = hash.getBytes(StandardCharsets.UTF_8);

   for (int i=0;i<2;i++) {

     md.update(digest);
     digest = md.digest();

     hash = String.format("%064x", new BigInteger(1,digest)).concat(R);
     System.out.println(hash);

     digest = hash.getBytes(StandardCharsets.UTF_8);
   }

Забудем об этой конкатенации на секунду.

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

Код 1:

   for (int i=0;i<2;i++) {

     md.update(digest);
     digest = md.digest();

   }

 hash = String.format("%064x", new BigInteger(1,digest));   
 System.out.println(hash);

Код 2:

   for (int i=0;i<2;i++) {

     md.update(digest);
     digest = md.digest();
     //convert hash to string
     hash = String.format("%064x", new BigInteger(1,digest));
     //convert string again to bytes
     digest = hash.getBytes(StandardCharsets.UTF_8);
   }

 System.out.println(hash);

Мой вопрос: как правильно декодировать хеш (Byte[]) в шестнадцатеричную строку каждый раз, чтобы объединить слово R и снова правильно кодировать в байты?


person Mavros91    schedule 30.03.2019    source источник
comment
ваш второй код добавляет R на первом этапе и на последнем этапе декодирования без хеширования. но ваш первый код добавляет R к строке и хеширует ее, чтобы подготовить к следующей итерации.   -  person Amin Heydari Alashti    schedule 30.03.2019
comment
Извините меня! я объяснил это неправильно раньше .. я отредактировал свой вопрос.   -  person Mavros91    schedule 30.03.2019


Ответы (2)


Фрагмент кода 1 правильный, но вам нужно добавить к нему оператор печати, чтобы получить ожидаемый результат. Однако для этого вам нужно использовать настоящий шестнадцатеричный кодировщик/декодер, который, к сожалению, не поставляется по умолчанию в java.util.


Вот переработанный пример без конкатенации, который я намеренно опустил, чтобы вам было чем заняться.

В коде используется относительно медленная, но легко запоминающаяся и читаемая функция toHex. Сначала BigInteger нужно построить BigInteger, что расточительно и, возможно, еще медленнее. Хотя код, кажется, работает правильно для 32-байтовых хеш-значений, я все же считаю, что код сложно поддерживать.

public static byte[] printHexadecimalHashIterations(byte[] input, int iterations)
{
    var digest = input.clone();

    MessageDigest md;
    try
    {
        md = MessageDigest.getInstance("SHA-256");
    }
    catch (NoSuchAlgorithmException e)
    {
        throw new IllegalStateException("SHA-256 hash should be available", e);
    }

    for (int i = 0; i < iterations; i++)
    {
        md.update(digest);
        digest = md.digest();

        printDigest("Intermediate hash", digest);
    }

    printDigest("Final hash", digest);

    return digest;
}

public static void printDigest(String hashType, byte[] digest)
{
    var digestInHex = toHex(digest);
    System.out.printf("%s: %s%n", hashType, digestInHex);
}

public static String toHex(byte[] data)
{
    var sb = new StringBuilder(data.length * 2);
    for (int i = 0; i < data.length; i++)
    {
        sb.append(String.format("%02X", data[i]));
    }
    return sb.toString();
}

public static void main(String[] args)
{
    printHexadecimalHashIterations("password".getBytes(StandardCharsets.UTF_8), 2);
}

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

Вы должны уметь отличать двоичные данные от шестнадцатеричных, которые являются просто представлением двоичных данных. Никогда не называйте двоичные данные «шестнадцатеричными», как в вопросе: это красный флаг, что вы не понимаете разницы.

Однако в вашем случае вам нужны только шестнадцатеричные числа, чтобы распечатать их на экране; вам вообще не нужно преобразовывать digest байтовый массив в шестнадцатеричный; он остается доступным. Так что можете просто продолжать.


Если вам нужно преобразовать это текстовое представление обратно в байты, вам потребуется выполнить шестнадцатеричное декодирование. Очевидно, вам снова понадобится хороший метод, который не использует для этого BigInteger. Существует множество библиотек (Guava, Apache Commons, Bouncy Castle), которые предоставляют хорошие шестнадцатеричные кодировщики/декодеры и хорошие вопросы/ответы на этот на SO . Оператор hash.getBytes(StandardCharsets.UTF_8) во фрагменте кода 2 выполняет не шестнадцатеричное декодирование, а кодирование символов.


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

Удачного программирования.


РЕДАКТИРОВАТЬ:

Чтобы выполнить вашу задачу, я бы сделал что-то вроде этого:

final byte[] passwordBytes = "password".getBytes(StandardCharsets.UTF_8);
final byte[] rBytes = "f@ghj!$g".getBytes(StandardCharsets.UTF_8);

digest.update(passwordBytes);
digest.update(rBytes);
byte[] currentHash = digest.digest();

for (int i = 1; i < iterations; i++)
{
    digest.update(currentHash);
    digest.update(rBytes);
    currentHash = digest.digest();
}
person Maarten Bodewes    schedule 30.03.2019
comment
Существует небольшая, но возможная вероятность того, что вам действительно нужно объединить шестнадцатеричную строку со строкой R, а затем закодировать ее. Однако это не отражено в вашем псевдокоде. Тем не менее, если это так, то человек, который создал требования/протокол, также не понимает кодировку. Вы должны вернуться и объяснить, что протокол плохой. - person Maarten Bodewes; 30.03.2019
comment
Я понял, что мне нужно каждый раз объединять шестнадцатеричную строку с R, но после вашего ответа я больше не уверен. - person Mavros91; 30.03.2019
comment
Что ж, если это так, то фрагмент кода 2 может быть правильным. Если нет смысла сильно хэшировать закодированные шестнадцатеричные числа. Также будет разница в хэшировании шестнадцатеричных символов верхнего и нижнего регистра, если назвать только одну проблему. Обратите внимание, что многие программисты и даже учителя говорят шестнадцатеричный, когда имеется в виду двоичный или байтовый. Лучше попросить разъяснений, если вы подозреваете, что это так. - person Maarten Bodewes; 30.03.2019
comment
Итак, вы предлагаете обновлять на каждой итерации byte[] R ( md.update(R) )? Что такое слово var в вашем коде? - person Mavros91; 30.03.2019
comment
var - это просто способ не вводить имя класса, он объявляет переменную local, которую затем необходимо немедленно назначить, чтобы компилятор мог присвоить класс. Он был введен в Java 10, чтобы упростить написание языка. Так, например. digest — это byte[], потому что это то, что возвращает input.clone(). - person Maarten Bodewes; 30.03.2019
comment
Итак, вы все равно учитесь, поэтому я показал в ответе, каким был бы мой ответ на заданный вам вопрос, надеясь, что это имелось в виду. Однако вам снова придется включить операторы печати и т.д. - person Maarten Bodewes; 30.03.2019
comment
Еще раз спасибо! Вы были очень полезны. Я думаю, что я плохо понял, что делает функция обновления, и это тоже было проблемой. - person Mavros91; 30.03.2019

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

person effitient    schedule 30.03.2019
comment
В фрагменте кода 1 или 2 не происходит конкатенация, поэтому я не получаю этот ответ. - person Maarten Bodewes; 30.03.2019
comment
Вы не понимаете, потому что вопрос был отредактирован настолько, что мой ответ больше не имеет смысла! Проверьте первоначальную версию. - person effitient; 30.03.2019
comment
Хорошо, достаточно честно. Этот первый пост не имел особого смысла, но ваш ответ правильный, если мы не будем учитывать правки. BlackMam, пожалуйста, попробуйте и опубликуйте только после того, как перечитаете свой вопрос, иначе может возникнуть путаница. - person Maarten Bodewes; 30.03.2019