How to Hash Passwords in Java
Part of the Hashing ToolkitArgon2Factory.create(Argon2Factory.Argon2Types.ARGON2id)argon2-jvm — the default create() call with no arguments returns Argon2i, not Argon2id
Explanation
argon2-jvm (de.mkammerer:argon2-jvm-nolibs on Maven/Gradle) wraps the real, native Argon2 reference implementation for the JVM. The one thing worth knowing before anything else:
Argon2 argon2 = Argon2Factory.create(); // defaults to Argon2i, NOT Argon2idCalling Argon2Factory.create() with no arguments gives you Argon2i — side-channel-resistant, but not the variant OWASP recommends for password storage. Getting Argon2id requires passing the type explicitly:
Argon2 argon2 = Argon2Factory.create(Argon2Factory.Argon2Types.ARGON2id);
char[] password = "correct horse battery staple".toCharArray();
try {
String hash = argon2.hash(2, 19456, 1, password); // iterations, memory (KiB), parallelism
boolean ok = argon2.verify(hash, password);
} finally {
argon2.wipeArray(password);
}Two details specific to the JVM here, not present in the Node.js or Python versions of this same code: passwords are passed as char[], not String — Java string literals can live in memory (and in the string pool) longer than you control, while a char[] can be explicitly overwritten once you're done with it. argon2.wipeArray(password) does exactly that, and belongs in a finally block so it runs even if hashing throws.
For what the iteration count, memory, and parallelism arguments actually control internally, see Argon2 Explained.
Valid examples
Argon2 argon2 = Argon2Factory.create(Argon2Factory.Argon2Types.ARGON2id); String hash = argon2.hash(2, 19456, 1, password);Explicitly requests Argon2id with OWASP-baseline parameters (iterations, memory in KiB, parallelism).
boolean ok = argon2.verify(hash, password); // password is a char[]Reads the algorithm, version, and parameters back out of the hash string itself.
try { ... } finally { argon2.wipeArray(password); }Overwrites the char[] password in memory once hashing/verification is done, in a finally block so it runs even on exception.
Invalid examples
Argon2 argon2 = Argon2Factory.create(); // Argon2i, not Argon2idThe no-argument factory method defaults to Argon2i — side-channel resistant, but not OWASP's recommended variant for password storage.
MessageDigest.getInstance("SHA-256").digest(password.getBytes())A raw SHA-256 digest — no salt, no cost factor. See Why SHA-256 Should Not Be Used for Passwords.
String password = "..."; // instead of char[]String is immutable in Java — it can't be explicitly wiped from memory the way a char[] can, so it may persist longer than intended, including in the string pool.