MessageDigest.getInstance: примеры (JAVA)

MessageDigest.getInstance: обзор и примеры использования
Раздел: Шифрование и хеширование (Cryptography, MessageDigest)
MessageDigest.getInstance(String algorithm): MessageDigest

Описание метода MessageDigest.getInstance

Метод MessageDigest.getInstance из пакета java.security возвращает объект типа java.security.MessageDigest, реализующий указанный алгоритм хеширования. Основное назначение - вычисление криптографических дайджестов (хешей) для данных.

Сигнатуры метода:

  • public static MessageDigest getInstance(String algorithm) throws NoSuchAlgorithmException
  • public static MessageDigest getInstance(String algorithm, String provider) throws NoSuchAlgorithmException, NoSuchProviderException
  • public static MessageDigest getInstance(String algorithm, Provider provider) throws NoSuchAlgorithmException

Аргументы и их значение:

  • algorithm - наименование алгоритма хеширования в виде строки, например "MD5", "SHA-1", "SHA-256", "SHA3-256". Значение чувствительно к регистру в зависимости от провайдера, обычно регистр не имеет значения для стандартных поставщиков.
  • provider (String) - имя поставщика безопасности, зарегистрированного в java.security.Security, например "SUN" или "BC" для BouncyCastle. Если указан, попытка будет использовать именно этого поставщика.
  • provider (Provider) - экземпляр java.security.Provider, позволяющий получить реализацию от конкретного объекта поставщика.

Возвращаемое значение:

  • Экземпляр MessageDigest, поддерживающий указанный алгоритм. С его помощью доступны методы update, digest, reset, clone (если поддерживается) и другие.

Исключения и поведение при ошибках:

  • NoSuchAlgorithmException - алгоритм не найден у доступных поставщиков.
  • NoSuchProviderException - указанный поставщик по имени не найден (сигнатура с именем поставщика).
  • Возможны NullPointerException при передаче null в качестве алгоритма.

Особенности:

  • Экземпляры MessageDigest не являются потокобезопасными. Для параллельного использования требуется синхронизация или отдельные экземпляры.
  • Поддерживаемые алгоритмы зависят от установленных провайдеров и версии JDK.

Примеры основных вариантов использования

Примеры иллюстрируют различные способы получения экземпляра MessageDigest и получение хеша строки "hello".

MD5 через стандартный вызов:

import java.security.MessageDigest;
import java.util.HexFormat;

public class Md5Example {
    public static void main(String[] args) throws Exception {
        MessageDigest md = MessageDigest.getInstance("MD5");
        byte[] digest = md.digest("hello".getBytes(java.nio.charset.StandardCharsets.UTF_8));
        System.out.println(HexFormat.of().formatHex(digest));
    }
}
5d41402abc4b2a76b9719d911017c592

SHA-256 и использование update:

import java.security.MessageDigest;
import java.util.HexFormat;

public class Sha256Example {
    public static void main(String[] args) throws Exception {
        MessageDigest md = MessageDigest.getInstance("SHA-256");
        md.update("he".getBytes());
        md.update("llo".getBytes());
        byte[] digest = md.digest();
        System.out.println(HexFormat.of().formatHex(digest));
    }
}
2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824

Запрос реализации у конкретного провайдера (имя провайдера может отсутствовать - тогда будет NoSuchProviderException):

import java.security.MessageDigest;

public class ProviderExample {
    public static void main(String[] args) throws Exception {
        // Если провайдера с таким именем нет, будет NoSuchProviderException
        MessageDigest md = MessageDigest.getInstance("SHA-256", "SUN");
        System.out.println(md.getAlgorithm() + " from " + md.getProvider().getName());
    }
}
SHA-256 from SUN

Похожие механизмы в Java

Несколько альтернатив и смежных API в экосистеме Java:

  • javax.crypto.Mac - для HMAC и других ключевых функций хеширования. Используется, когда требуется ключированная защита целостности.
  • java.security.Signature - для цифровой подписи, если требуется не только хеш, но и асимметричная подпись.
  • java.util.zip.CRC32 - не криптографический контроль целостности, быстрый и простой, но не предназначен для безопасности.
  • java.security.DigestInputStream - обертка для потокового вычисления дайджеста при чтении данных.
  • MessageDigest.isEqual - метод для сравнения хешей, реализующий безопасное сравнение против атак временного анализа.

Выбор зависит от цели: для неподписанных хешей подходит MessageDigest, для ключированных MAC - Mac, для подписи - Signature, для контрольных сумм без криптостойкости - CRC32.

Аналоги в других языках и их отличия

Краткие примеры по языкам; входная строка - "hello"; пример на SHA-256, где это уместно.

PHP (функция hash):

<?
echo hash('sha256', 'hello');
?>
2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824

JavaScript (Node.js):

const { createHash } = require('crypto');
console.log(createHash('sha256').update('hello').digest('hex'));
2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824

Python (hashlib):

import hashlib
print(hashlib.sha256(b'hello').hexdigest())
2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824

SQL (MySQL):

SELECT SHA2('hello', 256);
2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824

C# (.NET):

using System;
using System.Security.Cryptography;
using System.Text;

class Program {
    static void Main() {
        using var sha = SHA256.Create();
        var hash = sha.ComputeHash(Encoding.UTF8.GetBytes("hello"));
        Console.WriteLine(BitConverter.ToString(hash).Replace("-", "").ToLower());
    }
}
2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824

Go (crypto/sha256):

package main

import (
    "crypto/sha256"
    "encoding/hex"
    "fmt"
)

func main() {
    h := sha256.Sum256([]byte("hello"))
    fmt.Println(hex.EncodeToString(h[:]))
}
2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824

Lua (с использованием luaossl или openssl):

-- пример с luaossl
local digest = require('openssl').digest
print(digest.new('sha256'):final('hello'):tohex())
2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824

Отличия от Java: названия алгоритмов и API различаются, встроенные провайдеры и доступность алгоритмов зависят от среды. В Java используется концепция провайдеров, чего нет в большинстве простых реализаций других языков.

Типичные ошибки и примеры

Ниже наиболее распространенные ошибки при использовании MessageDigest.getInstance.

1) Неверное имя алгоритма - NoSuchAlgorithmException:

import java.security.MessageDigest;

public class BadAlgo {
    public static void main(String[] args) throws Exception {
        MessageDigest.getInstance("SHA-999");
    }
}
Exception in thread "main" java.security.NoSuchAlgorithmException: SHA-999 MessageDigest not available

2) Указан несуществующий поставщик - NoSuchProviderException:

import java.security.MessageDigest;

public class BadProvider {
    public static void main(String[] args) throws Exception {
        MessageDigest.getInstance("SHA-256", "NO_SUCH_PROVIDER");
    }
}
Exception in thread "main" java.security.NoSuchProviderException: no such provider: NO_SUCH_PROVIDER

3) NullPointerException при передаче null в качестве алгоритма:

MessageDigest.getInstance(null);
Exception in thread "main" java.lang.NullPointerException

4) Проблемы многопоточного доступа: использование одного экземпляра без синхронизации приводит к некорректным результатам. Пример неправильного параллельного использования может не выдавать исключение, а привести к неверному хешу. Решения: отдельные экземпляры для потоков или синхронизация, либо использование clone() если поддерживается.

5) Неправильная работа при повторном вызове digest без reset: при последовательных вызовах без reset предыдущие данные учитываются, что может вести к неожиданным результатам.

Изменения и история поддержки алгоритмов

Ключевые изменения, затрагивающие MessageDigest.getInstance:

  • Включение SHA-3 (например, SHA3-256) в стандартную поставку Java началось с Java 9; доступность зависит от версии JDK и провайдеров.
  • Списки поддерживаемых алгоритмов эволюционировали: устаревшие алгоритмы (например, MD2) во многих дистрибутивах получают ограничение или удаляются по соображениям безопасности.
  • Возможность подключения сторонних провайдеров (например, BouncyCastle) остается основным способом расширения набора алгоритмов.

Сам метод API в последних релизах JVM существенных изменений не претерпел, изменения касались прежде всего поставщиков и доступных алгоритмов.

Расширенные и редкие сценарии использования

Несколько более сложных примеров с пояснениями.

1) Вычисление дайджеста большого файла с DigestInputStream:

Пример java
import java.io.FileInputStream;
import java.security.DigestInputStream;
import java.security.MessageDigest;
import java.util.HexFormat;

public class FileDigest {
    public static void main(String[] args) throws Exception {
        MessageDigest md = MessageDigest.getInstance("SHA-256");
        try (DigestInputStream dis = new DigestInputStream(new FileInputStream("/path/to/file.bin"), md)) {
            byte[] buffer = new byte[8192];
            while (dis.read(buffer) != -1) { }
        }
        System.out.println(HexFormat.of().formatHex(md.digest()));
    }
}
(здесь будет SHA-256 хеш указанного файла)

2) Параллельные вычисления через clone(): если реализация поддерживает клонирование, это ускоряет работу при одинаковом начальном состоянии:

Пример java
MessageDigest md = MessageDigest.getInstance("SHA-256");
MessageDigest md1 = (MessageDigest) md.clone();
MessageDigest md2 = (MessageDigest) md.clone();
md1.update("part1".getBytes());
md2.update("part2".getBytes());
byte[] d1 = md1.digest();
byte[] d2 = md2.digest();
(два отдельных дайджеста для разных частей)

3) Использование BouncyCastle как провайдера для дополнительных алгоритмов:

Пример java
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import java.security.MessageDigest;
import java.security.Security;

public class BcExample {
    public static void main(String[] args) throws Exception {
        Security.addProvider(new BouncyCastleProvider());
        MessageDigest md = MessageDigest.getInstance("SHA3-256", "BC");
        byte[] d = md.digest("hello".getBytes());
        System.out.println(java.util.HexFormat.of().formatHex(d));
    }
}
(SHA3-256 хеш строки "hello")

4) Константно-временное сравнение хешей:

Пример java
byte[] a = ...;
byte[] b = ...;
boolean equal = MessageDigest.isEqual(a, b);
true или false без уязвимости по времени выполнения

5) Создание собственного провайдера: при наличии специфичных требований алгоритмов возможно написать и зарегистрировать собственную реализацию Provider, после чего MessageDigest.getInstance сможет возвращать реализацию от этого провайдера.

джава MessageDigest.getInstance function comments

En
MessageDigest.getInstance Returns a MessageDigest object that implements the specified digest algorithm