Skip to content

Hash Generator — Java source

Compute SHA-1, SHA-256, SHA-384 and SHA-512 hashes of any text at once, using the browser's native SubtleCrypto. Copy each digest. 100% client-side.

This is the Java implementation — the same logic the interactive tool runs, in a shareable, citable form.

// hash — SHA-1 / SHA-256 / SHA-384 / SHA-512 digest of a UTF-8 string.
//
// Language: Java 17 (standard library only)
// Source:   CosmoDev polyglot showcase port of the `hash` tool, ported from
//           src/tools/HashGenerator.tsx (the canonical TypeScript island).
// License:  display source — part of CosmoDev's polyglot tool pages.
//
// Unkeyed SHA digests via java.security.MessageDigest — the JDK's vetted
// provider set (typically backed by OpenSSL through the SunCryptoAPI native
// patches). All four names are standard JCA algorithm strings, so no external
// libraries are needed. Hex encoding uses Character.forDigit — no formatter
// and no third-party Hex class.
//
// Behavior mirrors the TypeScript reference: UTF-8 input, lowercase hex
// output, and rejection of unknown algorithm names. MessageDigest signals an
// unknown name with the checked NoSuchAlgorithmException; the port translates
// that into IllegalArgumentException — the Java standing of the ValueError the
// Python port raises — because from this API's viewpoint a bad name is a
// caller bug, not a recoverable environment failure. SHA-1 is offered for
// legacy compatibility only; it is not collision-resistant.

import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

/** Pure logic of the CosmoDev {@code hash} tool. */
public final class HashTool {

    /** Canonical algorithm names. The spellings match the TypeScript union so
     * the same string works across every port. */
    public static final String SHA1 = "SHA-1";
    public static final String SHA256 = "SHA-256"; // default algorithm
    public static final String SHA384 = "SHA-384";
    public static final String SHA512 = "SHA-512";

    /** The algorithm selected when a caller passes null or "" — the
     * optional-parameter default the TypeScript reference declares. */
    public static final String DEFAULT_ALGORITHM = SHA256;

    /** Every supported algorithm, in the order the React island renders them. */
    public static final List<String> ALGORITHMS = List.of(SHA1, SHA256, SHA384, SHA512);

    private HashTool() {
        // Static utility — no instances.
    }

    /**
     * Computes the named digest of {@code text} and returns it as lowercase
     * hex.
     *
     * <p>The text is UTF-8 encoded before hashing, so the result is correct
     * for arbitrary Unicode (emoji, accents, CJK). A null or empty
     * {@code algorithm} selects SHA-256. An unknown name throws
     * {@link IllegalArgumentException}.
     */
    public static String digest(String text, String algorithm) {
        String name = (algorithm == null || algorithm.isEmpty()) ? DEFAULT_ALGORITHM : algorithm;

        // The supported set is fixed by the tool. Checking it here — before
        // getInstance — rejects unknown names with IllegalArgumentException
        // (the Python port's ValueError) and stops the JCA from accepting
        // digests the tool does not offer: "MD5" passes the provider lookup
        // but is outside this API's contract.
        if (!ALGORITHMS.contains(name)) {
            throw new IllegalArgumentException("Unsupported hash algorithm: " + name);
        }

        MessageDigest md;
        try {
            // A fresh instance per call: MessageDigest is stateful, and
            // reusing one across digests would need reset() bookkeeping.
            md = MessageDigest.getInstance(name);
        } catch (NoSuchAlgorithmException e) {
            throw new IllegalArgumentException("Unsupported hash algorithm: " + name, e);
        }

        byte[] hash = md.digest(text.getBytes(StandardCharsets.UTF_8));

        // Two hex digits per byte, lowercase — Character.forDigit already
        // yields lowercase for bases 16 and below.
        StringBuilder hex = new StringBuilder(hash.length * 2);
        for (byte b : hash) {
            hex.append(Character.forDigit((b >> 4) & 0xf, 16));
            hex.append(Character.forDigit(b & 0xf, 16));
        }
        return hex.toString();
    }

    /**
     * Computes every supported digest of {@code text} — the Java shape of the
     * Record the React island renders. Each hash is computed independently.
     * The LinkedHashMap preserves the canonical algorithm order.
     */
    public static Map<String, String> hashAll(String text) {
        Map<String, String> all = new LinkedHashMap<>();
        for (String algorithm : ALGORITHMS) {
            all.put(algorithm, digest(text, algorithm));
        }
        return all;
    }
}

Also available in 13 other languages

Every CosmoDev tool ships its pure logic in TypeScript (web) and Go (CLI), with authored implementations in a dozen-plus languages — the same contract, ported. Compare all languages side by side →