Статьи Генератор паролей UUID MD5 Unix-время URL-encode Base64 Форматирование XML Ваш внешний IP Число прописью


Вычисление MD5-хэша

Вернуться назад

13 февраля 2020

Тэги: Spring алгоритмы Java 10

MD5 хэш (Message Digest 5) - это 128 битный алгоритм вычисления хэша от сообщения произвольной длины. Чаще всего хэш вычисляется от строки (для проверки паролей) и от файла (для контроля его целостности).

Следует сразу же заметить, что хранение паролей в виде md5-хэшей в настоящее время считается небезопасным, т.к. md5-хэш довольно сильно подвержен коллизиям. Коллизией называется ситуация, когда у двух исходных сообщений хэши равны. То есть для успешного прохождения проверки пароля, сохраняемого в виде md5-хэша, вам даже не обязательно знать именно этот пароль. Достаточно знать пароль, имеющий такой же хэш. Более того, хэши от простых паролей можно напрямую искать в Google.

Вычисление хэша от строки

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

public String getMd5Hash(String source) {
    try {
        var md = MessageDigest.getInstance("MD5");
        md.update(source.getBytes());
        byte[] digest = md.digest();
        return bytesToHex(digest);
    } catch (NoSuchAlgorithmException e) {
        throw new RuntimeException(e);
    }
}

Тут мы используем стандартный класс MessageDigest, который по имени алгоритма возвращает его реализацию. Если алгоритм по имени не найден, то метод getInstance() кидает исключение NoSuchAlgorithmException. Поскольку мы передаём в него константу «MD5», то точно знаем, что такой ошибки не возникнет. Поэтому обрачиваем это проверяемое исключение в RuntimeException, которое является непроверяемым, чтобы не «портить» сигнатуру метода.

Затем преобразуем исходную строку, от которой требуется посчитать хэш, в массив байт. Затем передаём этот массив в метод update(). В результате он возвращает хэш также в виде массива байт. Для удобства отображения массива байт в виде строки, напишем вспомогательный метод bytesToHex().

private String bytesToHex(byte[] bytes) {
    var builder = new StringBuilder();
    for (var b : bytes) {
        builder.append(String.format("%02x", b & 0xff));
    }
    return builder.toString();
}

Тут мы каждый байт преобразуем в строку, выполняя побитовое объединение этого байта с байтом 0xff. Во избежания создания лишних экземпляров строк, конкатенацию результата делаем с помощью StringBuilder, который в конце преобразуем в строку.

Байты в виде строки представляются в шестнадцатеричной (hexadecimal) системе счисления, поэтому содержат цифры от 0 до 9 и буквы от a до f.

Теперь вычислим хэш:

public static void main(String[] args) {
    var example = new PasswordExample();
    var hash = example.getMd5Hash("P@$$w0rd");
    System.out.println(hash); // c53e479b03b3220d3d56da88c4cace20
}

Если вы используете Spring, то всё то же самое можно сделать в одну строчку с помощью метода DigestUtils.md5DigestAsHex().

Вычисление хэша от файла

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

public String getMd5HashForFile(String filename) throws IOException {
    try {
        var md = MessageDigest.getInstance("MD5");
        var buffer = new byte[8192];
        try (var is = Files.newInputStream(Paths.get(filename))) {
            int read;
            while ((read = is.read(buffer)) > 0) {
                md.update(buffer, 0, read);
            }
        }
        byte[] digest = md.digest();
        return bytesToHex(digest);
    } catch (NoSuchAlgorithmException e) {
        throw new RuntimeException(e);
    }
}

Для чтения файла создаём InputStream в конструкции try-with-resources, которая автоматически закроет этот поток по завершению работы с ним. Сам поток получаем с помощью метода Files.newInputStream(). Затем в цикле считываем данные в созданный нами буфер - массив байт фиксированного размера. На каждой итерации вызываем метод update() для вычисления нового хэша на основе данных из буфера и прошлого значения хэша. Затем аналогично предыдущему примеру получаем значение хэша в виде массива байт и выводим его в виде строки.

Теперь передав в метод полный путь до проверяемого файла вы можете быстро вычислить его хэш. Если у вас Linux, то для проверки значения хэша можете использовать утилиту md5sum.