Hashing a Password in Java

1. Overview

In this tutorial, we’ll be discussing the importance of password hashing.

We’ll take a quick look at what it is, why it’s important, and some secure and insecure ways of doing it in Java.

2. What’s Hashing?

Hashing is the process of generating a string, or hash, from a given message using a mathematical function known as a cryptographic hash function.

While there are several hash functions out there, those tailored to hashing passwords need to have four main properties to be secure:

  1. It should be deterministic: the same message processed by the same hash function should always produce the same hash
  2. It’s not reversible: it’s impractical to generate a message from its hash
  3. It has high entropy: a small change to a message should produce a vastly different hash
  4. And it resists collisions: two different messages should not produce the same hash

A hash function that has all four properties is a strong candidate for password hashing since together they dramatically increase the difficulty in reverse-engineering the password from the hash.

Also, though, password hashing functions should be slow. A fast algorithm would aid brute force attacks in which a hacker will attempt to guess a password by hashing and comparing billions (or trillions) of potential passwords per second.

Some great hash functions that meet all these criteria arePBKDF2, BCrypt, and SCrypt. But first, let’s take a look at some older algorithms and why they are no longer recommended

3. Not Recommended: MD5

Our first hash function is the MD5 message-digest algorithm, developed way back in 1992.

Java’s MessageDigest makes this easy to calculate and can still be useful in other circumstances.

However, over the last several years, MD5 was discovered to fail the fourth password hashing property in that it became computationally easy to generate collisions. To top it off, MD5 is a fast algorithm and therefore useless against brute-force attacks.

Because of these, MD5 is not recommended.

4. Not Recommended: SHA-512

Next, we’ll look at SHA-512, which is part of the Secure Hash Algorithm family, a family that began with SHA-0 back in 1993.

4.1. Why SHA-512?

As computers increase in power, and as we find new vulnerabilities, then researchers derive new versions of SHA. Newer versions have a progressively longer length, or sometimes researchers publish a new version of the underlying algorithm.

SHA-512 represents the longest key in the third generation of the algorithm.

While there are now more secure versions of SHA, SHA-512 is the strongest that is implemented in Java.

4.2. Implementing in Java

Now, let’s have a look at implementing the SHA-512 hashing algorithm in Java.

First, we have to understand the concept of salt. Simply put, this is a random sequence that is generated for each new hash.

By introducing this randomness, we increase the hash’s entropy, and we protect our database against pre-compiled lists of hashes known as rainbow tables.

Our new hash function then becomes roughly:

salt <- generate-salt;
hash <- salt + ':' + sha512(salt + password)

4.3. Generating a Salt

To introduce salt, we’ll use the SecureRandom class from java.security:

SecureRandom random = new SecureRandom();
byte[] salt = new byte[16];
random.nextBytes(salt);

Then, we’ll use the MessageDigest class to configure the SHA-512 hash function with our salt:

MessageDigest md = MessageDigest.getInstance("SHA-512");
md.update(salt);

And with that added, we can now use the digest method to generate our hashed password:

byte[] hashedPassword = md.digest(passwordToHash.getBytes(StandardCharsets.UTF_8));

4.4. Why Is It Not Recommended?

When employed with salt, SHA-512 is still a fair optionbut there are stronger and slower options out there.

Also, the remaining options we’ll cover have an important feature: configurable strength.

5. PBKDF2, BCrypt, and SCrypt

PBKDF2, BCrypt, and SCrypt are three recommended algorithms.

5.1. Why Are Those Recommended?

Each of these is slow, and each has the brilliant feature of having a configurable strength.

This means that as computers increase in strength, we can slow down the algorithm by changing the inputs.

5.2. Implementing PBKDF2 in Java

Now, salts are a fundamental principle of password hashing, and so we need one for PBKDF2, too:

SecureRandom random = new SecureRandom();
byte[] salt = new byte[16];
random.nextBytes(salt);

Next, we’ll create a PBEKeySpec and a SecretKeyFactory which we’ll instantiate using the PBKDF2WithHmacSHA1 algorithm:

KeySpec spec = new PBEKeySpec(password.toCharArray(), salt, 65536, 128);
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");

The third parameter (65536) is effectively the strength parameter. It indicates how many iterations that this algorithm run for, increasing the time it takes to produce the hash.

Finally, we can use our SecretKeyFactory to generate the hash:

byte[] hash = factory.generateSecret(spec).getEncoded();

5.3. Implementing BCrypt and SCrypt in Java

So, it turns out that BCrypt and SCrypt support don’t yet ship with Java, though some Java libraries support them.

One of those libraries is Spring Security.

6. Password Hashing With Spring Security

Although Java natively supports both the PBKDF2 and SHA hashing algorithms, it doesn’t support BCrypt and SCrypt algorithms.

Luckily for us, Spring Security ships with support for all these recommended algorithms via the PasswordEncoder interface:

  • MessageDigestPasswordEncoder gives us MD5 and SHA-512
  • Pbkdf2PasswordEncoder gives us PBKDF2
  • BCryptPasswordEncoder gives us BCrypt, and
  • SCryptPasswordEncoder gives us SCrypt

The password encoders for PBKDF2, BCrypt, and SCrypt all come with support for configuring the desired strength of the password hash.

We can use these encoders directly, even without having a Spring Security-based application. Or, if we are protecting our site with Spring Security, then we can configure our desired password encoder through its DSL or via dependency injection.

And, unlike our examples above, these encryption algorithms will generate the salt for us internally. The algorithm stores the salt within the output hash for later use in validating a password.

7. Conclusion

So, we’ve taken a deep dive into password hashing; exploring the concept and its uses.

And we’ve taken a look at some historical hash functions as well as some currently implemented ones before coding them in Java.

Finally, we saw that Spring Security ships with its password encrypting classes, implementing an array of different hash functions.

As always, the code is available on GitHub.

Related posts:

Generating Random Dates in Java
Java Program to Implement Vector API
RegEx for matching Date Pattern in Java
Java Program to Generate Randomized Sequence of Given Range of Numbers
OAuth2 for a Spring REST API – Handle the Refresh Token in AngularJS
Hướng dẫn sử dụng String Format trong Java
Java Byte Array to InputStream
Java Program to Implement Heap
Java Program to Find Shortest Path Between All Vertices Using Floyd-Warshall’s Algorithm
Java Program to Check Cycle in a Graph using Graph traversal
Removing Elements from Java Collections
A Guide to Java SynchronousQueue
Show Hibernate/JPA SQL Statements from Spring Boot
Apache Tiles Integration with Spring MVC
Spring Boot - Sending Email
Registration with Spring Security – Password Encoding
Java Program to Check if a Given Binary Tree is an AVL Tree or Not
How to Read a File in Java
Fixing 401s with CORS Preflights and Spring Security
Quick Guide on Loading Initial Data with Spring Boot
Java Program to Implement Binomial Tree
Spring Cloud AWS – S3
Filtering and Transforming Collections in Guava
Một số nguyên tắc, định luật trong lập trình
Java Program to Search Number Using Divide and Conquer with the Aid of Fibonacci Numbers
Introduction to Spring Method Security
Guide to the Synchronized Keyword in Java
Xử lý ngoại lệ trong Java (Exception Handling)
Câu lệnh điều khiển vòng lặp trong Java (break, continue)
Java Program to Implement VList
Java Program to Implement Singly Linked List
Hướng dẫn Java Design Pattern – Factory Method