Hire Me
← All Writing Java

Hashing for Secure and Efficient Code

Java hashing guide — MD5, SHA-256, PBKDF2, and bcrypt with MessageDigest, Apache Commons Codec, Guava, and jBCrypt. When to use each algorithm, how to avoid common mistakes, and practical examples for data integrity and password storage.

Hashing is one of those topics that comes up constantly in backend development — from verifying data integrity in a Kafka pipeline to securing credentials in a web application. I’ve used Java’s hashing tools across most of my engagements: SHA-256 for trade event integrity checks in Mosaic’s financial data pipeline, bcrypt for user authentication in ESG’s metering platform, and MD5 for non-sensitive checksums in lower-stakes contexts. Here’s a practical guide to the key options Java offers, and when to use each.

What Is a Hash Function?

A hash function takes any input—text, numbers, even files—and spits out a fixed-size string called a hash or digest. It’s like a fingerprint: unique (mostly), deterministic (same input, same output), and fast. In ESG’s BOL Engine, I hashed user IDs to securely store credentials. In Mosaic’s pipeline, I used hashes to verify trade event integrity. Hash functions are one-way (you can’t reverse them) and aim for minimal collisions (different inputs producing the same hash).

ProTip: Always choose a hash function that matches your use case, speed for checksums, security for passwords.

Use Cases for Hash Functions

Hashing pops up everywhere in my projects:

Hashing Types

We will look at the following types of hash in this post:

  1. MD5 Message Digest: Fast but insecure (collisions found). I used it for quick checksums in Ribby Hall’s non-sensitive sync tasks.
  2. SHA Secure Hash Algorithm: Secure and widely used. I relied on it for trade event integrity in Mosaic’s pipeline.
  3. PBKDF2 Password-Based Key Derivative Function with Hmac-SHA1 (PBKDF2WithHmacSHA1): Slow by design, perfect for passwords. I used it in ESG’s user authentication.

Hashing with Java’s MessageDigest

Java’s java.security.MessageDigest is the go-to for MD5 and SHA:

import java.security.MessageDigest;
import java.math.BigInteger;

public String hashTradeId(String tradeId) throws NoSuchAlgorithmException {
    MessageDigest md = MessageDigest.getInstance("SHA-256");
    byte[] digest = md.digest(tradeId.getBytes());
    return String.format("%064x", new BigInteger(1, digest));
}

Used like:

String tradeId = "TRX12345";
String hash = hashTradeId(tradeId);
System.out.println(hash); // e.g., 2c26b46b68ffc68ff99b453c1d30413413422d706483bfa0f98a5e886266e7ae

ProTip: Always handle NoSuchAlgorithmException to avoid crashes if the algorithm isn’t supported.

Hashing with Apache Commons Codec

Apache Commons Codec simplifies hashing with readable output formats:

import org.apache.commons.codec.digest.DigestUtils;

public String hashPrice(String priceData) {
    return DigestUtils.sha256Hex(priceData);
}

Used like:

String priceData = "19.99:GBP";
String hash = hashPrice(priceData);
System.out.println(hash); // e.g., 8f434346648f6b96df89dda901c5176b10a6d83961dd3c1ac88b59b2dc327aa4

Add the dependency:

<dependency>
    <groupId>commons-codec</groupId>
    <artifactId>commons-codec</artifactId>
    <version>1.17.1</version>
</dependency>

Hashing with Guava

Google’s Guava library offers robust hashing utilities:

import com.google.common.hash.Hashing;

import java.nio.charset.StandardCharsets;

public String hashConfig(String config) {
    return Hashing.sha256().hashString(config, StandardCharsets.UTF_8).toString();
}

Used like:

String config = "data.db:1000";
String hash = hashConfig(config);
System.out.println(hash); // e.g., 5e884898da28047151d0e56f8dc6292773603d0d6aabbdd62a11ef721d1542d8

Add the dependency:

<dependency>
    <groupId>com.google.guava</groupId>
    <artifactId>guava</artifactId>
    <version>33.3.1-jre</version>
</dependency>

Password Hashing with bcrypt

For passwords, bcrypt is my pick due to its adaptive design:

import org.mindrot.jbcrypt.BCrypt;

public String hashPassword(String password) {
    return BCrypt.hashpw(password, BCrypt.gensalt(12));
}

public boolean checkPassword(String password, String hashed) {
    return BCrypt.checkpw(password, hashed);
}

Used like:

String password = "user123";
String hashed = hashPassword(password);
System.out.println(hashed); // e.g., $2a$12$WqX8z7kZ1qY9z3mN4pL8vO...

boolean valid = checkPassword("user123", hashed); // true

Add the dependency:

<dependency>
    <groupId>org.mindrot</groupId>
    <artifactId>jbcrypt</artifactId>
    <version>0.4</version>
</dependency>

ProTip: Use bcrypt’s gensalt() with a work factor (e.g., 12) to balance security and performance for password hashing.

Common Pitfalls and Best Practices

Hashing is powerful, but I’ve tripped up:

ProTip: Profile hashing performance with VisualVM in high-throughput systems like Mosaic’s pipeline to catch bottlenecks.

Conclusion

Hashing has been a cornerstone of my Java projects. In ESG’s BOL Engine, bcrypt kept user passwords secure. In Mosaic’s pipeline, SHA-256 ensured trade event integrity. In Co-op’s reports and Ribby Hall’s sync, hashing sped up lookups and verified data. Whether you’re securing credentials or optimizing lookups, Java’s hashing tools have you covered. Start small: try SHA-256 for a checksum or bcrypt for a password.

Got a hashing trick that saved your day? Ping me here, I’d love to hear your story!

Java MessageDigest

Share LinkedIn →
Samuel Jackson

Samuel Jackson

Senior Java Back End Developer & Contractor

Senior Java Back End Developer — Betfair Exchange API specialist, Spring Boot, AWS, and event-driven architecture. 20+ years delivering high-performance systems across betting, finance, energy, retail, and government. Available for Java contracting.