Криптографическое хеширование с MessageDigest
Хешируйте пароли с помощью SHA-256, добавляйте соль для защиты от атак по радужным таблицам и безопасно сравнивайте дайджесты.
«Криптографическое хеширование с MessageDigest» — бесплатный урок Java Academy на CoddyKit. Это урок 1 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения Java Academy, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс Java Academy содержит 4 уроков всего.
Что такое криптографическое хеширование
Криптографическая хеш-функция преобразует данные любого размера в дайджест фиксированного размера. Она односторонняя (её нельзя обратить), детерминирована и устойчива к коллизиям. Хеширование используется для хранения паролей, проверки целостности данных и создания цифровых подписей.
API MessageDigest
MessageDigest.getInstance("SHA-256") возвращает средство вычисления хеша SHA-256. Вызовите digest(bytes), чтобы вычислить хеш и получить массив байтов.
MessageDigest md = MessageDigest.getInstance("SHA-256");
byte[] hash = md.digest("password123".getBytes(StandardCharsets.UTF_8));
String hex = HexFormat.of().formatHex(hash);
System.out.println(hex); // 64-char hex stringРаспространённые алгоритмы
JDK поддерживает следующие алгоритмы: MD5 (взломан — никогда не используйте его для защиты), SHA-1 (устарел для целей защиты), SHA-256 (стандартный), SHA-512 (более стойкий), SHA3-256 (современный). Для нового кода всегда используйте SHA-256 или SHA-512.
MessageDigest.getInstance("SHA-256"); // recommended
MessageDigest.getInstance("SHA-512"); // stronger
MessageDigest.getInstance("SHA3-256"); // SHA-3 family
// Never: MessageDigest.getInstance("MD5"); // cryptographically brokenПреобразование хеша в шестнадцатеричную строку
Результат в виде массива байтов необходимо преобразовать в шестнадцатеричную строку для хранения или сравнения. Используйте HexFormat (Java 17+) или String.format в более старых версиях.
// Java 17+:
String hex = HexFormat.of().formatHex(hash);
// Older Java:
StringBuilder sb = new StringBuilder();
for (byte b : hash) sb.append(String.format("%02x", b));
String hex = sb.toString();Соль: защита от радужных таблиц
Радужная таблица сопоставляет распространённые пароли с их хешами. Добавление соли присоединяет к каждому паролю случайное значение перед хешированием, делая радужные таблицы бесполезными. Храните соль вместе с хешем.
SecureRandom random = new SecureRandom();
byte[] salt = new byte[16];
random.nextBytes(salt);
MessageDigest md = MessageDigest.getInstance("SHA-256");
md.update(salt);
byte[] hash = md.digest("password".getBytes(StandardCharsets.UTF_8));
// Store: Base64.encode(salt) + ":" + Base64.encode(hash)Хеширование паролей — используйте BCrypt
SHA-256 с солью лучше обычного SHA-256, но всё ещё работает быстро — злоумышленник может проверять миллиарды вариантов в секунду. Для паролей используйте медленный KDF: BCryptPasswordEncoder (Spring Security), PBKDF2 или Argon2.
// Spring Security — proper password hashing:
PasswordEncoder encoder = new BCryptPasswordEncoder(12); // cost factor 12
String hash = encoder.encode("password123");
boolean match = encoder.matches("password123", hash); // trueПроверка целостности файла
Вычисляйте хеш файла, чтобы обнаружить повреждение или несанкционированное изменение. Сравнивайте вычисленный хеш с эталонным значением, которому доверяете.
MessageDigest md = MessageDigest.getInstance("SHA-256");
try (InputStream is = Files.newInputStream(Path.of("app.jar"))) {
byte[] buf = new byte[8192];
int n;
while ((n = is.read(buf)) > 0) md.update(buf, 0, n);
}
String hash = HexFormat.of().formatHex(md.digest());
System.out.println("SHA-256: " + hash);Безопасное сравнение хешей
Используйте MessageDigest.isEqual() для сравнения за постоянное время, чтобы предотвратить атаки по времени выполнения. Никогда не используйте String.equals() для сравнения хешей.
byte[] expected = computeHash(storedHash);
byte[] actual = computeHash(inputPassword);
boolean match = MessageDigest.isEqual(expected, actual); // constant-timeПоследовательное обновление хеша
Несколько раз вызовите md.update(bytes) перед md.digest(), чтобы хешировать данные, поступающие частями (в потоковом режиме).
MessageDigest md = MessageDigest.getInstance("SHA-256");
md.update("part1".getBytes());
md.update("part2".getBytes());
byte[] hash = md.digest(); // hash of "part1" + "part2"Кодирование хешей для хранения
Храните двоичные хеши в формате Base64 (он короче шестнадцатеричного представления) или в виде шестнадцатеричных строк. Для SHA-256 Base64 использует 43 символа, а шестнадцатеричное представление — 64. Оба варианта корректны — главное, придерживаться единообразия.
byte[] hash = md.digest(data);
String base64 = Base64.getEncoder().encodeToString(hash); // 44 chars (with padding)
String hex = HexFormat.of().formatHex(hash); // 64 charsDigestInputStream для потоковой обработки
Оберните InputStream в DigestInputStream, чтобы вычислять хеш во время чтения и не загружать весь файл в память.
MessageDigest md = MessageDigest.getInstance("SHA-256");
try (DigestInputStream dis = new DigestInputStream(Files.newInputStream(path), md)) {
dis.transferTo(OutputStream.nullOutputStream());
}
String hash = HexFormat.of().formatHex(md.digest());Быстрая проверка
Почему SHA-256 с солью всё ещё не рекомендуется для хранения паролей?
Итоги
Используйте MessageDigest.getInstance("SHA-256") для проверки целостности данных. Всегда добавляйте соль к паролям, но для их хранения используйте BCrypt или Argon2, а не необработанный SHA. Для предотвращения атак по времени выполнения используйте сравнение за постоянное время (MessageDigest.isEqual).
Часто задаваемые вопросы
Урок «Криптографическое хеширование с MessageDigest» бесплатный?
Да — полный текст урока «Криптографическое хеширование с MessageDigest» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс Java Academy, подпишись на CoddyKit PRO. Курс Java Academy содержит 4 уроков всего.
Чему я научусь в уроке «Криптографическое хеширование с MessageDigest»?
Хешируйте пароли с помощью SHA-256, добавляйте соль для защиты от атак по радужным таблицам и безопасно сравнивайте дайджесты. Ты практикуешь Java Academy с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.
Нужен ли мне опыт, чтобы начать Java Academy?
Предыдущий опыт не требуется. Java Academy на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 1 из 4.
Сколько времени занимает урок «Криптографическое хеширование с MessageDigest»?
Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.
Можно ли писать и запускать код в этом уроке Java Academy?
Да. Каждый урок Java Academy включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.
Все уроки этого курса
- Криптографическое хеширование с MessageDigest
- Симметричное шифрование AES
- HMAC-SHA256 для целостности сообщений
- JWT: структура, создание и проверка