How do you compute a cryptographic hash in Java using the MessageDigest API? Walk through the getInstance / update / digest lifecycle.
answer
- getInstance → update* → digest
- digest() auto-resets the engine
- Always getBytes(StandardCharsets.UTF_8)
- Output length fixed by algorithm (SHA-256 = 32 bytes)
- Not thread-safe
basics
~10 sCall MessageDigest.getInstance("SHA-256") to get an engine, feed bytes with update(...), then call digest() to get the final hash as a byte[]. digest() finishes the computation and resets the engine for reuse.
solid answer
~40 sMessageDigest is the JCA engine for cryptographic hashing. You obtain an instance with the static factory MessageDigest.getInstance("SHA-256"), which throws NoSuchAlgorithmException if no provider supplies that algorithm. You then feed input incrementally with update(byte[]) — useful for streaming large data — and finally call digest(), which returns the fixed-length hash as a byte[] (32 bytes for SHA-256). There is also a convenience digest(byte[]) that does update + digest in one call. After digest() the object is automatically reset, so you can reuse it for the next message. Always specify the charset when hashing a String (e.g. text.getBytes(StandardCharsets.UTF_8)) so the result is platform-independent. MessageDigest is not thread-safe; use one instance per thread or create a fresh one per call.
code
java · 13 linesimport java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.nio.charset.StandardCharsets;
import java.util.HexFormat;
public byte[] sha256(String text) throws NoSuchAlgorithmException {
MessageDigest md = MessageDigest.getInstance("SHA-256");
// Streaming-friendly: update can be called repeatedly.
md.update(text.getBytes(StandardCharsets.UTF_8));
byte[] hash = md.digest(); // 32 bytes; md is now auto-reset
System.out.println(HexFormat.of().formatHex(hash));
return hash;
}go deeper
Can name the three steps (getInstance, update, digest) and produce a SHA-256 hash of a string with an explicit UTF-8 charset.
Knows digest() auto-resets, can hash streamed data via repeated update, handles NoSuchAlgorithmException, and renders bytes to hex/Base64.
Discusses thread-safety, provider selection, fixed output length, and correctly distinguishes integrity hashing from password storage.
Reasons about algorithm agility (configurable algorithm names), provider/policy implications across the fleet, and sets organization-wide guidance against MD5/SHA-1 and against MessageDigest-for-passwords.
## What problem this solves A **cryptographic hash function** takes an arbitrary-length input (bytes) and produces a fixed-length output called a **digest** (e.g. 256 bits = 32 bytes for SHA-256). Good cryptographic hashes are *deterministic* (same input → same output), *one-way* (you cannot recover the input from the digest), and *collision-resistant* (it is computationally infeasible to find two inputs with the same digest). They are used for integrity checks (did this file change?), fingerprinting, and as a building block inside signatures and HMACs. ## The JCA and what MessageDigest is Java exposes cryptography through the **JCA (Java Cryptography Architecture)** — a provider-based framework. `java.security.MessageDigest` is the *engine class* for hashing. "Engine class" means it is an abstract façade: you never call a constructor; instead a **provider** (a registered implementation, e.g. the built-in `SUN` provider) supplies the actual algorithm behind the scenes. This indirection lets you swap algorithms or providers without changing your code. ## The lifecycle, step by step 1. **Obtain an instance** with the static factory method: ```java MessageDigest md = MessageDigest.getInstance("SHA-256"); ``` `getInstance` takes the algorithm name as a string. If no installed provider offers that algorithm, it throws the *checked* exception `NoSuchAlgorithmException`. Standard names include `SHA-256`, `SHA-384`, `SHA-512`, `SHA3-256`, and the legacy `MD5` / `SHA-1` (which are broken for security use). There is an overload `getInstance("SHA-256", "SUN")` to pin a specific provider. 2. **Feed input** with `update`: ```java md.update(part1); // byte[] md.update(part2); ``` `update` accumulates bytes into the running computation. You can call it many times, which is how you hash data that arrives in chunks (a file read in blocks, a network stream) without holding it all in memory. 3. **Finish** with `digest`: ```java byte[] hash = md.digest(); // 32 bytes for SHA-256 ``` `digest()` performs the final padding and computation and returns the digest. **Important: after `digest()` the engine is automatically reset** to its initial state, so the same object can immediately hash a new message. There is a convenience overload `md.digest(input)` that is equivalent to `md.update(input); md.digest();` for the common one-shot case. ## Hashing a String correctly A `String` is characters, not bytes, so you must encode it explicitly and *always specify a charset*: ```java byte[] hash = md.digest(text.getBytes(StandardCharsets.UTF_8)); ``` Using the no-arg `getBytes()` relies on the platform default charset, which can differ between machines and silently produce different digests — a classic portability bug. ## Useful facts and edge cases - **Output length is fixed by the algorithm**, regardless of input size: SHA-256 → 32 bytes, SHA-512 → 64 bytes. - **`getDigestLength()`** returns that length in bytes. - **`reset()`** abandons any buffered input and returns the engine to its initial state (useful if you started feeding data and want to start over without calling `digest`). - **Not thread-safe.** A single `MessageDigest` instance holds mutable internal state, so two threads sharing one will corrupt each other's computation. Use a fresh instance per call, a `ThreadLocal`, or per-thread instances. - The raw `byte[]` is usually rendered to **hex** or **Base64** for display/storage (e.g. `HexFormat.of().formatHex(hash)` in Java 17+). ## What this API is NOT for MessageDigest is a *raw, fast, unsalted* hash. **Do not use it to store passwords** — fast hashes are brute-forceable. For passwords use a deliberately slow, salted KDF such as PBKDF2, bcrypt, scrypt, or Argon2. MessageDigest is for *integrity and fingerprinting*, not secret-at-rest protection.
- What does digest() do to the instance after it returns the hash?It automatically resets the engine to its initial state, so the same object can immediately be reused to hash a new message; you do not need to recreate it.
- Why must you pass a charset when hashing a String?A String is characters; getBytes() with no charset uses the platform default, which can vary across machines and yield different digests. Specify UTF-8 for a deterministic, portable result.
saying these in an interview costs you the question
- Forgetting that digest() resets the instance and thinking you must create a new one each time
- Using getBytes() without a charset (platform-dependent digests)
- Sharing one MessageDigest across threads
- Using MessageDigest to store passwords instead of a slow salted KDF