DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Please enter at least three characters to search
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Zones

Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks

Last call! Secure your stack and shape the future! Help dev teams across the globe navigate their software supply chain security challenges.

Modernize your data layer. Learn how to design cloud-native database architectures to meet the evolving demands of AI and GenAI workloads.

Releasing software shouldn't be stressful or risky. Learn how to leverage progressive delivery techniques to ensure safer deployments.

Avoid machine learning mistakes and boost model performance! Discover key ML patterns, anti-patterns, data strategies, and more.

Related

  • How to Get Plain Text From Common Documents in Java
  • Reading an HTML File, Parsing It and Converting It to a PDF File With the Pdfbox Library
  • Express Hibernate Queries as Type-Safe Java Streams
  • Exploring Hazelcast With Spring Boot

Trending

  • Automating Data Pipelines: Generating PySpark and SQL Jobs With LLMs in Cloudera
  • The Human Side of Logs: What Unstructured Data Is Trying to Tell You
  • 5 Subtle Indicators Your Development Environment Is Under Siege
  • Testing SingleStore's MCP Server
  1. DZone
  2. Coding
  3. Java
  4. Hashing Passwords in Java With BCrypt

Hashing Passwords in Java With BCrypt

In this tutorial, we show you how to use jBCrypt to hash your passwords, and up your security game in these troubled times for the cybersecurity world.

By 
Bill O'Neil user avatar
Bill O'Neil
·
Aug. 03, 17 · Tutorial
Likes (11)
Comment
Save
Tweet
Share
100.6K Views

Join the DZone community and get the full member experience.

Join For Free

BCrypt is a one-way salted hash function based on the Blowfish cipher. It provides several enhancements over plain text passwords (unfortunately this still happens quite often) and traditional hashing algorithms (md5). It wouldn't be accurate to say BCrypt is the best way to store passwords but it should be good enough. Algorithms such as PBKDF2 could be used as a more thoroughly tested algorithm but BCrypt is commonly used as well. jBCrypt is a Java implementation of BCrypt.

BCrypt Features

  • Not plain text - Not only do plain text passwords compromise your website if the database is breached but they can also compromise other websites for the users. Unfortunately, a lot of users share passwords across websites.
  • One way hashing - BCrypt is a one-way hash function to obfuscate the password such that it is not stored in plain text.
  • Salted hashing - Generating random bytes (the salt) and combining it with the password before hashing creates unique hashes across each user's password. If two users have the same password they will not have the same password hash. This is to prevent rainbow table attacks which can reverse hashed passwords using common hashing functions that do not utilize a salt.
  • Logarithmic iterations - The hashing function is executed many times sequentially which can be increased exponentially, which is known as key stretching. This is to make the function CPU intensive which makes it more secure against brute force attacks.
  • Updatable iterations - As CPUs become faster so do brute force attacks. Since BCrypt stores the number of iterations as part of the hash it's possible to verify a password and then increase its strength by generating a new hash with a higher number of iterations.

jBCrypt Example Wrapper

We will be wrapping the standard jBCrypt with our own methods to allow auto updating the iterations on the fly.

public class UpdatableBCrypt {
    private static final Logger log = LoggerFactory.getLogger(UpdatableBCrypt.class);

    private final int logRounds;

    public UpdatableBCrypt(int logRounds) {
        this.logRounds = logRounds;
    }

    public String hash(String password) {
        return BCrypt.hashpw(password, BCrypt.gensalt(logRounds));
    }

    public boolean verifyHash(String password, String hash) {
        return BCrypt.checkpw(password, hash);
    }

    public boolean verifyAndUpdateHash(String password, String hash, Function<String, Boolean> updateFunc) {
        if (BCrypt.checkpw(password, hash)) {
            int rounds = getRounds(hash);
            // It might be smart to only allow increasing the rounds.
            // If someone makes a mistake the ability to undo it would be nice though.
            if (rounds != logRounds) {
                log.debug("Updating password from {} rounds to {}", rounds, logRounds);
                String newHash = hash(password);
                return updateFunc.apply(newHash);
            }
            return true;
        }
        return false;
    }

    /*
     * Copy pasted from BCrypt internals :(. Ideally a method
     * to exports parts would be public. We only care about rounds
     * currently.
     */
    private int getRounds(String salt) {
        char minor = (char)0;
        int off = 0;

        if (salt.charAt(0) != '$' || salt.charAt(1) != '2')
            throw new IllegalArgumentException ("Invalid salt version");
        if (salt.charAt(2) == '$')
            off = 3;
        else {
            minor = salt.charAt(2);
            if (minor != 'a' || salt.charAt(3) != '$')
                throw new IllegalArgumentException ("Invalid salt revision");
            off = 4;
        }

        // Extract number of rounds
        if (salt.charAt(off + 2) > '$')
            throw new IllegalArgumentException ("Missing salt rounds");
        return Integer.parseInt(salt.substring(off, off + 2));
    }
}

Static Hashing Utility Class

Let's make a static utility class to handle everything for us. Ideally, we will periodically update the number of iterations in this class based on the recommended intervals.

// This should be updated every year or two.
private static final UpdatableBCrypt bcrypt = new UpdatableBCrypt(11);

public static String hash(String password) {
    return bcrypt.hash(password);
}

public static boolean verifyAndUpdateHash(String password, String hash, Function<String, Boolean> updateFunc) {
    return bcrypt.verifyAndUpdateHash(password, hash, updateFunc);
}

BCrypt Java Example With Updatable Iterations

Putting it all together.

// Mini function to test updates.
String[] mutableHash = new String[1];
Function<String, Boolean> update = hash -> { mutableHash[0] = hash; return true; };

String hashPw1 = Hashing.hash("password");
log.debug("hash of pw1: {}", hashPw1);
log.debug("verifying pw1: {}", Hashing.verifyAndUpdateHash("password", hashPw1, update));
log.debug("verifying pw1 fails: {}", Hashing.verifyAndUpdateHash("password1", hashPw1, update));
String hashPw2 = Hashing.hash("password");
log.debug("hash of pw2: {}", hashPw2);
log.debug("verifying pw2: {}", Hashing.verifyAndUpdateHash("password", hashPw2, update));
log.debug("verifying pw2 fails: {}", Hashing.verifyAndUpdateHash("password2", hashPw2, update));

UpdatableBCrypt oldHasher = new UpdatableBCrypt(7);
String oldHash = oldHasher.hash("password");
log.debug("hash of oldHash: {}", oldHash);
log.debug("verifying oldHash: {}, hash upgraded to: {}",
          Hashing.verifyAndUpdateHash("password", oldHash, update),
          mutableHash[0]);

Notice how the first and second password are the same but the hashes are different. We are also able to upgrade an older version of a BCrypt password using a low number of iterations to a higher number on the fly. In the real world, the passed in Function would be updating the database.

2017-08-02 01:07:38.757 [main] DEBUG com.stubbornjava.common.Hashing - hash of pw1: $2a$11$MXOOO1JYngri2arcL6Cic.KuBujhqgz.B2ri6szqN2/cfsdiQa7se
2017-08-02 01:07:38.936 [main] DEBUG com.stubbornjava.common.Hashing - verifying pw1: true
2017-08-02 01:07:39.108 [main] DEBUG com.stubbornjava.common.Hashing - verifying pw1 fails: false
2017-08-02 01:07:39.284 [main] DEBUG com.stubbornjava.common.Hashing - hash of pw2: $2a$11$TckEYxY/0DPf6OfpQhXKP./hl45UlgXYs0jQFsZZCwBEjUCo7bUKy
2017-08-02 01:07:39.466 [main] DEBUG com.stubbornjava.common.Hashing - verifying pw2: true
2017-08-02 01:07:39.647 [main] DEBUG com.stubbornjava.common.Hashing - verifying pw2 fails: false
2017-08-02 01:07:39.660 [main] DEBUG com.stubbornjava.common.Hashing - hash of oldHash: $2a$07$LMVTcMYGZZ6ORi4oPUGPAe3v8Kqpl.UCzKO2s8yfFN6c9vfM4szKW
2017-08-02 01:07:39.671 [main] DEBUG c.s.common.UpdatableBCrypt - Updating password from 7 rounds to 11
2017-08-02 01:07:39.848 [main] DEBUG com.stubbornjava.common.Hashing - verifying oldHash: true, hash upgraded to: $2a$11$1MvimUN3YO4G9DGY8G7rqOyPIjyye7jMYdIu8/BXL7t.e9CECj5Oa
Java (programming language) Plain text

Published at DZone with permission of Bill O'Neil. See the original article here.

Opinions expressed by DZone contributors are their own.

Related

  • How to Get Plain Text From Common Documents in Java
  • Reading an HTML File, Parsing It and Converting It to a PDF File With the Pdfbox Library
  • Express Hibernate Queries as Type-Safe Java Streams
  • Exploring Hazelcast With Spring Boot

Partner Resources

×

Comments
Oops! Something Went Wrong

The likes didn't load as expected. Please refresh the page and try again.

ABOUT US

  • About DZone
  • Support and feedback
  • Community research
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 100
  • Nashville, TN 37211
  • support@dzone.com

Let's be friends:

Likes
There are no likes...yet! 👀
Be the first to like this post!
It looks like you're not logged in.
Sign in to see who liked this post!