The Java pattern for producing a SHA-512 hash with a salt is: request the algorithm with java.security.MessageDigest.getInstance("SHA-512"), concatenate random salt bytes produced by java.security.SecureRandom with the password bytes, and render the resulting 64-byte digest as 128 lowercase hexadecimal characters or as standard padded Base64. SHA-512, defined in NIST FIPS 180-4, operates on 1024-bit blocks with eight 64-bit state words and emits 512 output bits, and Java's standard provider returns a digest byte-for-byte identical to every other conformant implementation, including a browser-based SHA-512 hash generator. The salt itself does not change the algorithm; it changes the input bytes. A salted SHA-512 result is therefore reproducible only when the exact salt bytes are known and the exact pre-image is preserved, which is the main reason salt-related comparison failures are common. A single salt prepended or appended to the input is not, on its own, a safe password storage scheme: raw SHA-512 is intentionally fast, and fast hashing enables offline guessing attacks. Production password storage should use a memory-hard key derivation function such as Argon2id, scrypt, or bcrypt, with a per-user random salt, an optional pepper, and tuned cost parameters.

How Java Computes a SHA-512 Digest
Java's MessageDigest is an engine-based API: you request an algorithm by name, feed it bytes, and read out the digest. For SHA-512 the call is MessageDigest.getInstance("SHA-512"), after which digest(inputBytes) returns exactly 64 bytes regardless of input length. Encoding those 64 bytes as two-character-per-byte lowercase hex gives a 128-character string; encoding them as standard padded Base64 gives 88 characters. Both representations describe the same bytes, so converting between them is loss-free.
The same input bytes passed to any conformant SHA-512 implementation — another JDK, OpenSSL, Python's hashlib, or an in-browser generator — must produce the identical 64 bytes. If two outputs differ, the difference is in the input bytes, not in the algorithm. The implementation in the SunJCE provider performs the FIPS 180-4 message schedule: it pads the message into 1024-bit blocks, expands each block into an 80-word schedule, and updates eight 64-bit state values across the full message. The concatenation of those final state values is the 512-bit digest.
A worked reference: the empty string is a valid SHA-512 input and produces a 128-character lowercase hex string beginning cf83e135, as verified by NIST FIPS 180-4 and reproduced with OpenSSL. If you add a single byte to the input — even an invisible one such as a trailing newline or a UTF-8 byte-order mark — no part of the 128-character output stays the same.
Where the Salt Fits in the Java Code
The standard Java pattern is to generate a per-user random salt with SecureRandom (typically 16 bytes), concatenate the salt with the password bytes in a fixed order, feed the combined bytes to MessageDigest("SHA-512"), and store the salt alongside the digest so the verifier can recompute the hash. A minimal version looks like:
SecureRandom rng = new SecureRandom(); byte[] salt = new byte[16]; rng.nextBytes(salt); MessageDigest md = MessageDigest.getInstance("SHA-512"); md.update(salt); byte[] hash = md.digest(password.getBytes(StandardCharsets.UTF_8));
To verify, recompute the digest with the stored salt and compare it to the stored hash in constant time using MessageDigest.isEqual (available since Java 7) or a dedicated constant-time comparator. The salt's only job is to make every user's pre-image different so that a precomputed lookup table — a rainbow table — cannot be reused across the database. See SHA-512 Password Hash With Salt: What the Output Means for the storage-format details that go alongside this code.
Three implementation details decide whether two SHA-512 implementations agree byte for byte:
- Concatenation order. Salt-then-password and password-then-salt produce different digests even when the same salt and password bytes are used. Pick one and stick to it.
- String-to-bytes encoding. Java's String.getBytes() with no argument uses the platform default charset, which is rarely UTF-8 on production servers. Always pass StandardCharsets.UTF_8.
- Normalization. Any Unicode normalization (NFC, NFKC), case folding, or trimming changes the pre-image and therefore the digest. Apply it explicitly before hashing, or not at all.
Why Raw SHA-512 With Salt Is Not Enough for Passwords
A salt blocks rainbow tables but it does not slow down a single guess. Raw SHA-512 is designed to be fast: a modern consumer GPU computes billions of SHA-512 evaluations per second, and dedicated ASICs run far faster. An attacker who steals a database of (salt, sha512(salt || password)) pairs can iterate through candidate passwords at that rate, trying each one against every row. Per-user salts force the attacker to spend the same compute per row, but the wall-clock cost per guess is still the speed of one SHA-512 call.
Production password storage needs a memory-hard, parameterized construction that makes each evaluation expensive in both time and memory and that resists GPU and ASIC parallelism. The widely accepted choices are:
- Argon2id — the current OWASP recommendation and the strongest general-purpose option.
- scrypt — a memory-hard alternative with a long track record.
- bcrypt — a CPU-and-salt-only construction that is acceptable but no longer the strongest.
- PBKDF2-HMAC-SHA-512 — a CPU-bound construction used only when the previous options are unavailable, with a high iteration count.
Each takes the salt, the password, a cost parameter (and, for Argon2id and scrypt, a memory parameter) and produces a stored value that encodes all of those inputs so verification can reproduce the computation. None of them is a single MessageDigest call. In Java, Bouncy Castle (bcprov) ships production Argon2 and scrypt support, Spring Security ships bcrypt and Argon2 support, and the JDK itself does not currently include a production-grade password KDF.
| Algorithm | Digest bits | Digest bytes | Lowercase hex characters |
|---|---|---|---|
| SHA-256 | 256 | 32 | 64 |
| SHA-384 | 384 | 48 | 96 |
| SHA-512 | 512 | 64 | 128 |
| SHA-512/256 | 256 | 32 | 64 |
| SHA-512/224 | 224 | 28 | 56 |
All five are separate algorithms specified by FIPS 180-4. SHA-512/256 and SHA-512/224 use different initial hash values and a different truncation rule than full SHA-512 — they are not produced by simply cutting the first 256 or 224 bits off a full SHA-512 digest. The Sha512 Hash Generator always returns full 512 bits, so a 128-character hex result is expected; anything shorter indicates a different algorithm.
Verify the Digest With the Sha512 Hash Generator
The Sha512 Hash Generator produces the same 64-byte digest as a conformant Java MessageDigest("SHA-512") call when fed the same bytes, which makes it a quick way to confirm that your Java code is hashing the bytes you think it is. The browser performs the calculation locally, so the message or file you select is not uploaded.
- Decide which mode you need. Text mode converts a string to UTF-8 and reports the resulting byte count; file mode hashes raw bytes without interpreting a filename, character set, or MIME type — useful for binary artifacts such as JARs and PDFs, within the browser's 100 MB per-input limit.
- In Java, capture the exact bytes that reach MessageDigest.digest. For a unit test, print them as hex so you can paste the same byte sequence back as text.
- Paste the unsalted version of those bytes into the generator as UTF-8 text, preserving capitalization, line breaks, and any trailing whitespace.
- Generate and copy the 128-character hex output, or the padded Base64 if your reference is in Base64.
- Compare every character against the digest produced by Java for the same bytes. If the two match character for character, your MessageDigest wiring is correct. If they differ, the mismatch is upstream of the algorithm.
Diagnosing a Java Output That Does Not Match a Reference
When a Java SHA-512 result fails to match a published digest or a reference system, the difference is almost always upstream of the algorithm itself. The common sources of mismatch:
- Wrong encoding. String.getBytes() with no argument uses the platform default charset. On most production servers that is not UTF-8. Pass StandardCharsets.UTF_8 explicitly.
- Trailing or leading whitespace. A trailing newline is part of the input. The strings "abc" and "abc\n" produce completely different digests.
- BOM insertion. Some file readers prepend a UTF-8 byte-order mark (the bytes EF BB BF) without making it visible. That BOM becomes part of the hash input.
- Variant confusion. Your output is full 512 bits. SHA-512/256 and SHA-512/224 use different initial values and a different truncation; they will not match even when the input is identical. SHA-384 is a separate algorithm with a shorter output.
- Salt included in the comparison. A reference digest computed without your salt will never match the salted digest. Strip the salt from the input — or use a deterministic test salt — before comparing.
The tool's golden cases — empty text, the string abc, the FIPS multi-block message, punctuation variations, UTF-8 text, arbitrary binary bytes, and common ASCII — give you standard pre-images whose digests you can compare your Java output against. Expected values are checked against the FIPS 180-4 test vectors and the NIST CAVP Secure Hashing program, and they are independently reproduced with OpenSSL.
When You Actually Need HMAC, Not a Raw Digest
Sometimes "hash with salt" in Java code is shorthand for "use a keyed construction". If the threat model includes an attacker who can read the database but cannot read the application secret, the right construction is HMAC-SHA-512, not raw SHA-512. HMAC combines a secret key with the message before hashing, so the digest cannot be recomputed without the key. In Java the call is Mac.getInstance("HmacSHA512") with a SecretKeySpec wrapping the key bytes; the output is again 64 bytes and renders as 128 hex characters.
The HMAC Generator covers HMAC-SHA-256, HMAC-SHA-384, and HMAC-SHA-512 and operates on exact UTF-8 or hexadecimal key bytes, which is useful for verifying your Java HMAC output against a known fixture. Note that HMAC-SHA-512 produces a different 64-byte digest from raw SHA-512 of the same message bytes, so an HMAC output will never match a raw-SHA-512 reference even with the same input.
If the threat model instead requires proving who sent the message — origin authentication rather than integrity — use a digital signature such as RSA-PSS, ECDSA, or Ed25519. A hash is a fingerprint; a signature is a fingerprint plus cryptographic proof of authorship. For password storage specifically, return to Argon2id, scrypt, or bcrypt with a per-user salt; the Sha512 Hash Generator remains the right tool only for verifying the raw, unsalted, full 512-bit digest.