Строка Java ME MD5 с использованием надувного замка - не может хешироваться несколько раз

Я заметил, что многие мои поисковые запросы в Google приводили меня сюда, поэтому я подумал, что, возможно, я смогу позаимствовать ваши подходящие умы :)

Я работаю над генератором одноразовых паролей для мобильного устройства (а также над веб-сайтом для входа) в рамках своей диссертации на третьем курсе.

Используя библиотеку org.bouncycastle.crypto.digests.MD5Digest, я беру байтовый массив (из строкового ввода пользователя), а затем хэширую его X раз. Это также известно как шлейфовое соединение хеш-строк или метод шифрования лампортов.

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

См. Код ниже:

private String generateHash(String OTP, int loopNum)
{
      byte[] secretBytes = OTP.getBytes();

      for (int x = 0; x < loopNum; x++)
      {
          byte[] tempStore = new byte[16];
          tempStore = hash(secretBytes);
          secretBytes = tempStore;
      }

      return convertToHex(secretBytes);
}

public byte[] hash(byte[] secretBytes)
{
        org.bouncycastle.crypto.digests.MD5Digest digest = new org.bouncycastle.crypto.digests.MD5Digest();

        digest.reset();

        // Update MD5 digest with user secret in byte format
        digest.update(secretBytes, 0, secretBytes.length);

        // get length of digest to initialise new md5 byte array
        int length = digest.getDigestSize();

        // create md5 byte array using length
        byte[] md5 = new byte[length];

        // calculate MD5 hash, using md5 byte array, 0 for buffer offset
        digest.doFinal(md5, 0);

        return md5;
}


private static String convertToHex(byte[] data) {
        StringBuffer buf = new StringBuffer();
        String Hex;
        String formattedHex;
        for (int i = 0; i < data.length; i++) {
            int halfbyte = (data[i] <<< 4) & 0x0F;
            int two_halfs = 0;
            do {
                if ((0 <= halfbyte) && (halfbyte <= 9))
                    buf.append((char) ('0'  + halfbyte));
                else
                    buf.append((char) ('a'+  (halfbyte - 10)));
                halfbyte = data[i] & 0x0F;
            } while(two_halfs++ < 1);
        }

        Hex = buf.toString();

        formattedHex = "\n"  + Hex.substring(0, 4) +  " " + Hex.substring(4, 8) + " " + Hex.substring(8, 12) + " "
               + Hex.substring(12, 16) +  " " + Hex.substring(16, 20) +  " "  +Hex.substring(20, 24) + " "
               + Hex.substring(24, 28) +  " " + Hex.substring(28, 32);
        return formattedHex;
    }

Я думаю, что это тоже;

  1. Дайджест не возвращает правильный массив байтов
  2. Конвертер Hex неправильно конвертирует это

Я тестирую, используя секрет: A, который имеет следующие выходы MD5:

  1. 7fc56270e7a70fa81a5935b72eacbe29
  2. 8f28f2e7231860115d2a8cacba019dbe (это должно быть 4cbd6d53280de25e04712c7434a70642)

Заранее большое спасибо за вашу помощь :)

p.s. Я проверяю его по PHP md5. Может ли это быть проблемой?


person Andy Garbett    schedule 03.02.2010    source источник
comment
Это может быть проблема с методом php md5. Я протестировал ваш код с реализацией Java MD5, и он отлично работает. Похоже, что с вашей шестнадцатеричной кодировкой проблем нет, поскольку она такая же, как и у commons-codec. Можете ли вы отредактировать свое сообщение, включив в него используемый вами PHP-код?   -  person Kevin    schedule 03.02.2010
comment
Не делайте этого: (проект диссертации 3-го года, пожалуйста, hlp)   -  person Chris    schedule 04.02.2010
comment
Извините, у меня заканчивались символы, я знаю, что это так раздражает, когда люди не печатают правильно. И часть диссертации, ну, у меня 11 недель, и я застрял на четыре дня, так что мне действительно нужна была помощь. К счастью, я думаю, вы, ребята, могли его взломать! Спасибо миллион и один :)   -  person Andy Garbett    schedule 04.02.2010
comment
Также позвольте мне добавить, я только неделю назад начал работать с Java. Я вырос в C #, так что все немного по-другому!   -  person Andy Garbett    schedule 04.02.2010
comment
Добро пожаловать! Если вы не возражаете, не забудьте поставить галочку рядом с тем ответом, который, по вашему мнению, был лучшим.   -  person Dean J    schedule 05.02.2010


Ответы (2)


MD5, когда применяется к входу, состоящему из одного байта значения 0x41 ('A'), дает 16-байтовый выход, который при печати в шестнадцатеричном формате равен 7fc56270e7a70fa81a5935b72eacbe29.

Если вы примените MD5 к этим 16 байтам, вы должны получить 8f28f2e7231860115d2a8cacba019dbe, и это то, что вы получите.

Теперь, если вы считаете, что MD5 применяется к 32-байтовой строке, которая является кодировкой ASCII символьной строки «7fc56270e7a70fa81a5935b72eacbe29», тогда это дает 4cbd6d53280de25e04712c7434a70642. Поэтому я думаю, что ваш Java-код подходит (для этого) и что ваша путаница возникает из-за того, как вы передаете входные данные в свой тестовый код на основе PHP. Вы пишете 7fc562... и думаете об этом как о «один байт значения 0x7f, затем один байт значения 0xc5, затем ...», но код PHP принимает это как «один байт значения 0x37 (код ASCII для '7') , затем один байт значения 0x66 (код ASCII для 'f'), затем ... ".

В системе Linux попробуйте следующее:

$ printf A | md5sum
7fc56270e7a70fa81a5935b72eacbe29  -
$ printf 7fc56270e7a70fa81a5935b72eacbe29 | md5sum
4cbd6d53280de25e04712c7434a70642  -
$ printf "\x7f\xc5\x62\x70\xe7\xa7\x0f\xa8\x1a\x59\x35\xb7\x2e\xac\xbe\x29" | md5sum
8f28f2e7231860115d2a8cacba019dbe  -

В качестве примечаний на стороне:

  • Остерегайтесь OTP.getBytes(). Он преобразует строку в байты с использованием кодировки, зависящей от языкового стандарта. Это будет использовать UTF-8, UTF-16, ISO-8859-1, ... в зависимости от конфигурации системы, обычно привязанной к «системному языку». Ваш код будет действовать по-разному с одной и той же строкой, что редко бывает хорошей идеей. Вместо этого используйте OTP.getBytes("UTF-8"), который будет вычислять одни и те же байты независимо от локальной конфигурации.
  • В вашем цикле хеширования есть бесполезные мантры. Например, вы выделяете 16-байтовый массив, который никогда не используете.
  • В Java считается плохим стилем программирования, когда имя переменной начинается с заглавной буквы. Если вы планируете показывать свой код в школьном контексте, вам следует переименовать Hex в hex.
  • Когда halfByte получается как результат «& 0x0F», тогда он обязательно содержит значение от 0 до 15. Тест «0 <= halfByte» не нужен.
person Thomas Pornin    schedule 03.02.2010
comment
Бинго. Метод php md5 () принимает только строку, а не необработанные байты. - person Kevin; 04.02.2010
comment
Во-первых, большое спасибо, ребята, за ваш ответ, я искренне ценю его. Я понимаю, что с двухбайтовыми массивами мне не нужны два. Я просто пытался убедиться, что байтовый массив не перезаписывается неправильно и т. Д. Есть ли способ заставить php принимать байтовый массив? Я не знаю, как это исправить. - person Andy Garbett; 04.02.2010

Большое спасибо за вашу помощь, проблема заключалась в том, что мой java md5 не вел себя как php MD5.

Я нашел решение проблемы, которое в основном (в Java) берет массив байтов и преобразует его в шестнадцатеричную строку, а ЗАТЕМ получает байты для этой строки, которая затем MD5'd, а не использует массив байтов un hex'd. См. Решения ниже

См. Результат: http://forums.sun.com/thread.jspa?forumID=9&threadID=718781.

static String byteArrayToHexString(byte byteValues[]) {
        byte singleChar = 0;
        if (byteValues == null || byteValues.length <= 0)
            return null;

        String entries[] = { "0", "1", "2", "3", "4", "5", "6", "7", "8", "9",
                "a", "b", "c", "d", "e", "f" };

        StringBuffer out = new StringBuffer(byteValues.length * 2);

        for (int i = 0; i < byteValues.length; i++) {
            singleChar = (byte) (byteValues[i] & 0xF0);
            singleChar = (byte) (singleChar >>> 4);
            // shift the bits down
            singleChar = (byte) (singleChar & 0x0F);
            out.append(entries[(int) singleChar]); 
            singleChar = (byte) (byteValues[i] & 0x0F); 
            out.append(entries[(int) singleChar]);
        }
        String rslt = new String(out);
        return rslt;
    }

Большое спасибо всем, кто разместил, не могу поблагодарить вас!

person Andy Garbett    schedule 03.02.2010
comment
Просто убедитесь, что вы задокументировали это поведение алгоритма - здесь вы выполняете не MD5, а шестнадцатеричное кодирование MD5 +. - person Paŭlo Ebermann; 30.06.2011