Как хэшировать несколько раз и объединять строки в каждом раунде - PullRequest
0 голосов
/ 30 марта 2019

Я пишу программу, которая объединяет слово 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 и снова кодировать в байтыправильный путь?

Ответы [ 2 ]

1 голос
/ 30 марта 2019

Фрагмент кода 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.

Счастливого программирования.


EDIT:

Для выполнения вашей задачи я бы сделал что-то вроде этого:

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();
}
1 голос
/ 30 марта 2019

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

...