MessageDigest.digest: примеры (JAVA)
MessageDigest.digest(byte[] input): byte[]Общее описание
Класс java.security.MessageDigest предоставляет реализацию алгоритмов хеширования (MD5, SHA-1, SHA-256 и т. п.). Метод digest выполняет вычисление итогового дайджеста (хеша) от ранее переданных данных или переданного массива и возвращает результат в виде байтового массива или записывает его в предоставленный буфер. Метод полезен для контроля целостности, хранения проверочных сумм и подготовки входных данных для криптографических схем.
Поведение метода зависит от сигнатуры. После выполнения вычисления внутреннее состояние экземпляра сбрасывается к начальному (аналогично вызову reset()), поэтому повторный вызов без новых вызовов update вернёт дайджест пустого состояния.
Сигнатуры и семантика аргументов
- byte[] digest()
Выполняет завершающий шаг на основе накопленных ранее вызовов
update. Возвращает новый массив байтов длиной, равной размеру дайджеста (например, 16 для MD5, 32 для SHA-256). - byte[] digest(byte[] input)
Комбинирует вызов
update(input)иdigest()в одной операции. Возвращает новый массив байтов с дайджестом входных данных. - int digest(byte[] buf, int offset, int len) throws DigestException
Записывает вычисленный дайджест в предоставленный массив
buf, начиная с позицииoffset. Параметрlenуказывает максимально доступный размер для записи (т. е. число байтов, которое можно разместить начиная сoffset). Метод возвращает количество записанных байтов (обычно равное длине дайджеста). Если доступного места недостаточно, генерируетсяjava.security.DigestException. После записи внутреннее состояние сбрасывается.
Замечания об аргументах и возвращаемых значениях:
- Длина возвращаемого массива или записанного диапазона равна
getDigestLength()у конкретного экземпляра MessageDigest. - Методы нечувствительны к кодировке байтов; при хешировании строк следует явно указывать кодировку, например
getBytes(StandardCharsets.UTF_8). - MessageDigest не является потокобезопасным. Для параллельных вычислений требуется отдельный экземпляр на поток или синхронизация.
Короткие примеры
Ниже приведены минимальные примеры основных вариантов использования метода digest с результатами.
1) digest() после update:
import java.security.MessageDigest;
import java.util.HexFormat;
MessageDigest md = MessageDigest.getInstance("SHA-256");
md.update("abc".getBytes(java.nio.charset.StandardCharsets.UTF_8));
byte[] hash = md.digest();
System.out.println(HexFormat.of().formatHex(hash));
ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad
2) digest(byte[] input) в одну строчку:
MessageDigest md = MessageDigest.getInstance("MD5");
byte[] md5 = md.digest("hello".getBytes(java.nio.charset.StandardCharsets.UTF_8));
System.out.println(javax.xml.bind.DatatypeConverter.printHexBinary(md5).toLowerCase());
5d41402abc4b2a76b9719d911017c592
3) digest(buf, offset, len) - запись в заранее выделенный массив:
MessageDigest md = MessageDigest.getInstance("SHA-1");
byte[] input = "abc".getBytes(java.nio.charset.StandardCharsets.UTF_8);
byte[] buf = new byte[64];
md.update(input);
int written = md.digest(buf, 8, buf.length - 8);
System.out.println("written=" + written);
// вывести хекс от части массива
String hex = java.util.HexFormat.of().formatHex(buf, 8, 8 + written);
System.out.println(hex);
written=20 a9993e364706816aba3e25717850c26c9cd0d89d
Похожие возможности в Java
- java.security.DigestInputStream - потоковая обёртка для автоматического обновления MessageDigest при чтении данных. Подходит для больших файлов, чтобы не загружать всё в память.
- javax.crypto.Mac - реализация HMAC (ключевой хеш). Предпочтительнее для аутентификации сообщений, когда требуется секретный ключ.
- java.util.zip.CRC32 - контрольная сумма, не криптографическая, но быстрая и компактная. Используется для обнаружения случайных ошибок, не для безопасности.
- MessageDigestSpi - для реализации собственных провайдеров алгоритмов. Используется редко, когда нужен кастомный алгоритм.
Выбор зависит от задачи: для криптографической целостности - MessageDigest или Mac (в случае ключа). Для потоковой обработки больших объёмов предпочтительнее DigestInputStream.
Аналоги в других языках
Короткие примеры и отличия от Java:
- PHP
echo hash('sha256', 'abc');ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad
hash выполняет вычисление в одну операцию; для итеративного обновления доступен hash_init / hash_update / hash_final.
- JavaScript (Node.js)
const crypto = require('crypto'); const h = crypto.createHash('sha256').update('abc').digest('hex'); console.log(h);ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad
В браузере доступна crypto.subtle.digest, возвращающая Promise с ArrayBuffer.
- Python
import hashlib print(hashlib.sha256(b'abc').hexdigest())ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad
hashlib поддерживает incremental update через объект хеша.
- SQL (пример для MySQL/Postgres)
-- MySQL SELECT SHA2('abc', 256); -- PostgreSQL (extension pgcrypto) SELECT digest('abc', 'sha256');ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad \xBA7816... (варианты представления зависят от СУБД)
- C# (.NET)
using System.Security.Cryptography; using System.Text; using var sha = SHA256.Create(); byte[] b = sha.ComputeHash(Encoding.UTF8.GetBytes("abc")); Console.WriteLine(BitConverter.ToString(b).Replace("-", "").ToLower());ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad
- Go
import ("crypto/sha256"; "fmt") h := sha256.Sum256([]byte("abc")) fmt.Printf("%x\n", h)ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad
- Kotlin
val md = java.security.MessageDigest.getInstance("SHA-256") val digest = md.digest("abc".toByteArray(Charsets.UTF_8)) println(java.util.HexFormat.of().formatHex(digest))ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad
Отличия в основном в API: Java предоставляет объектно-ориентированный интерфейс с incremental update, как и многие другие языки. Различия в ошибкоустойчивости, потоковой обработке и представлении результата (raw bytes, hex, base64).
Типичные ошибки и примеры
- Отсутствие явной кодировки строк
Пример:
"текст".getBytes()может дать разный результат на разных платформах. Рекомендуется указыватьStandardCharsets.UTF_8.MessageDigest md = MessageDigest.getInstance("SHA-256"); byte[] h = md.digest("привет".getBytes()); System.out.println(java.util.HexFormat.of().formatHex(h));(результат зависит от платформы; на UTF-8 будет детерминирован)
- Неправильная работа с digest(buf, offset, len)
Если доступного места меньше, возникает DigestException.
MessageDigest md = MessageDigest.getInstance("SHA-256"); md.update("abc".getBytes(java.nio.charset.StandardCharsets.UTF_8)); byte[] buf = new byte[10]; int written = md.digest(buf, 0, buf.length); // недостаточно места System.out.println(written);Exception in thread "main" java.security.DigestException: Insufficient space in the output buffer to store the digest
- Попытка многопоточного совместного использования одного экземпляра
MessageDigest не потокобезопасен. Несинхронизированный доступ приведёт к некорректным результатам.
// два потока параллельно вызывают update(...) на одном экземпляре MessageDigest // результат хеша будет непредсказуем(неопределённый, возможны неверные дайджесты)
- Ошибочное сравнение байтов через Arrays.equals в криптографическом контексте
Arrays.equals может быть подвержен тайминг-атакам; для критичных задач предпочтительна MessageDigest.isEqual (реализована с постоянным временем).
Изменения в API
API MessageDigest в Java остаётся стабильным на протяжении долгого времени. Основные изменения связаны с добавлением новых алгоритмов поставщиками и поддержкой современных стандартов через провайдеров безопасности. Формальные сигнатуры методов digest() и связанной функциональности не претерпели существенных изменений в последних версиях платформы.
Отдельно: поддержка удобных утилит для представления байтов (например, java.util.HexFormat) появилась в более новых версиях JDK; это облегчает преобразование результата в текст.
Продвинутые и редкие сценарии
Несколько подробных примеров с пояснениями.
1) Инкрементальное хеширование больших файлов через DigestInputStream:
import java.io.FileInputStream;
import java.security.DigestInputStream;
import java.security.MessageDigest;
import java.util.HexFormat;
MessageDigest md = MessageDigest.getInstance("SHA-256");
try (DigestInputStream dis = new DigestInputStream(new FileInputStream("big.bin"), md)) {
byte[] buffer = new byte[8192];
while (dis.read(buffer) != -1) {
// чтение и одновременное обновление md
}
}
byte[] digest = md.digest();
System.out.println(HexFormat.of().formatHex(digest));
(хеш файла big.bin)
Пояснение: DigestInputStream освобождает от необходимости самостоятельно вызывать update при чтении кусками.
2) Снимок состояния хеша через клонирование:
MessageDigest md = MessageDigest.getInstance("SHA-256");
md.update("prefix".getBytes(java.nio.charset.StandardCharsets.UTF_8));
MessageDigest snapshot = (MessageDigest) md.clone();
// продолжение для варианта A
md.update("A".getBytes(java.nio.charset.StandardCharsets.UTF_8));
byte[] a = md.digest();
// продолжение для варианта B на основе snapshot
snapshot.update("B".getBytes(java.nio.charset.StandardCharsets.UTF_8));
byte[] b = snapshot.digest();
System.out.println(java.util.HexFormat.of().formatHex(a));
System.out.println(java.util.HexFormat.of().formatHex(b));
(два разных хеша: prefix+A и prefix+B)
Пояснение: клонирование позволяет эффективно ветвить вычисления без повторного хеширования префикса.
3) Итеративное хеширование (простая реализация устойчивости к перебору, не заменяет PBKDF2):
MessageDigest md = MessageDigest.getInstance("SHA-256");
byte[] data = "password".getBytes(java.nio.charset.StandardCharsets.UTF_8);
int iterations = 10000;
byte[] cur = data;
for (int i = 0; i < iterations; i++) {
cur = md.digest(cur);
}
System.out.println(java.util.HexFormat.of().formatHex(cur));
(результат зависит от количества итераций)
Пояснение: для реального KDF применяется javax.crypto.SecretKeyFactory и PBKDF2WithHmacSHA256. Простой цикл может быть полезен для некоторых задач, но не для стандартизованного хранения паролей.
4) Константное время сравнения 결과 с MessageDigest.isEqual:
byte[] a = MessageDigest.getInstance("SHA-256").digest("x".getBytes());
byte[] b = MessageDigest.getInstance("SHA-256").digest("y".getBytes());
boolean eq = MessageDigest.isEqual(a, b);
System.out.println(eq);
false
Пояснение: использование isEqual снижает риск тайминг-атак при сравнении хешей в аутентификационных сценариях.
5) Комбинация нескольких алгоритмов (например, двойной хеш):
MessageDigest md1 = MessageDigest.getInstance("SHA-256");
MessageDigest md2 = MessageDigest.getInstance("SHA-1");
byte[] first = md1.digest("data".getBytes(java.nio.charset.StandardCharsets.UTF_8));
byte[] combined = md2.digest(first);
System.out.println(java.util.HexFormat.of().formatHex(combined));
(результат SHA-1 от SHA-256("data"))
Пояснение: подобные комбинации специфичны и редко повышают безопасность по сравнению с правильно подобранным современным алгоритмом.
джава MessageDigest.digest function comments
- джава MessageDigest.digest - аргументы и возвращаемое значение
- Функция java MessageDigest.digest - описание
- MessageDigest.digest - примеры
- MessageDigest.digest - похожие методы на java
- MessageDigest.digest на javascript, c#, python, php
- MessageDigest.digest изменения java
- Примеры MessageDigest.digest на джава