GithubHelp home page GithubHelp logo

amdelamar / jhash Goto Github PK

View Code? Open in Web Editor NEW
67.0 8.0 7.0 581 KB

๐Ÿ”‘ Password hashing utility in Java

Home Page: https://amdelamar.com/jhash

License: Other

Java 100.00%
hash password-hash pbkdf2 salt pepper bcrypt scrypt

jhash's Introduction

Jhash

Maven Central Javadoc Build Codecov

Password hashing utility in Java. It can hash passwords with PBKDF2 hmac SHA1/SHA256/SHA512, BCRYPT, or SCRYPT, and it salts automatically and has a pepper option.

Download

Maven:

<dependency>
    <groupId>com.amdelamar</groupId>
    <artifactId>jhash</artifactId>
    <version>2.2.0</version>
</dependency>

Gradle:

dependencies {
    compile 'com.amdelamar:jhash:2.2.0'
}

SBT:

libraryDependencies ++= Seq(
  "com.amdelamar" % "jhash" % "2.2.0"
)

Or Download the latest release.

Usage

Easy hash and verification...

import com.amdelamar.jhash.Hash;

char[] password = "Hello World!".toCharArray();

// salt + hash a password. (pbkdf2 hmac sha1)
String hash = Hash.password(password).create();
// Example: pbkdf2sha1:64000:18:24:n:LZXY631xphycV5kaJ2WY0RRDqSfwiZ6L:uOw06jt6FvimXSxEJipYYHsQ

// Save the enitre string somewhere safe...

// Verify Login
if(Hash.password(password).verify(correctHash)) {
    // Passwords match. Login successful!
}

More Options...

// pbkdf2 hmac sha512 + salt
String hash = Hash.password(password).algorithm(Type.PBKDF2_SHA512).create();
// Returns: pbkdf2sha512:64000:18:24:n:EbroMczUKuBRx5sy+hgFQyHmqk2iNtt5:Ml8pGxc3pYoh1z5fkk5rfjM9

// pbkdf2 hmac sha256 + salt + pepper
String hash = Hash.password(password).pepper(pepper).algorithm(Type.PBKDF2_SHA256).create();
// Returns: pbkdf2sha256:64000:18:24:y:J84o+zGuJebtj99FiAMk9pminEBmoEIm:4hoNRxgrn79lxujYIrNUXQd1

// pbkdf2 hmac sha512 + salt + pepper + higher salt length
String hash = Hash.password(password).pepper(pepper).algorithm(Type.PBKDF2_SHA512).saltLength(36).create();
// Returns: pbkdf2sha512:64000:18:36:y:v+tqRNA5B4cAxbZ4aUId/hvrR+FlS1d8:/R851fqvd7HItsSr0vJEupBf

// bcrypt + salt
String hash = Hash.password(password).algorithm(Type.BCRYPT).create();
// Example: bcrypt:13:66:16:n::$2a$10$YQ9urAM3RKuDtl1XaF99HrdpoIlB6ZhfaGR1T4yS4jlfMSPyeXehE.0Dway

// bcrypt + salt + pepper
String hash = Hash.password(password).pepper(pepper).algorithm(Type.BCRYPT).create();
// Example: bcrypt:13:66:16:y::$2a$10$UlxpnyYwYmmlLgl7YVGonN9H74ffEttiD1O2uMy8q5Y7YgJc8.YsRa3yOM6

// scrypt + salt
String hash = Hash.password(password).algorithm(Type.SCRYPT).create();
// Example: scrypt:16384:80:24:n::$s0$e0801$+nNFxTV9IHyN0cPKn/ORDA==$uPrBpPBQm7GgX+Vcc/8zuFNJZ+8XqDMylpLrOjv6X8w=

// scrypt + salt + pepper
String hash = Hash.password(password).pepper(pepper).algorithm(Type.SCRYPT).create();
// Example: scrypt:16384:80:24:y::$s0$e0801$iHSTF05OtGCb3BiaFTZ3BA==$QANWx2qBzMzONIQEXUJTWnNX+3wynikSkGJdO9QvOx8=

// scrypt + salt + pepper + higher complexity factor
String hash = Hash.password(password).pepper(pepper).algorithm(Type.SCRYPT).factor(1048576).create();
// Example: scrypt:16384:80:24:y::$s0$e0801$iHSTF05OtGCb3BiaFTZ3BA==$QANWx2qBzMzONIQEXUJTWnNX+3wynikSkGJdO9QvOx8=

Now verify the passwords match. Even if you use a stronger algorithm, longer salt length, or increase the complexity factor, you don't need to provide that information when you verify() because the hash output has those values already. But if you used a pepper, you need to provide that when verifying.

// Verify Login
if(Hash.password(password).verify(correctHash)) {
    // Passwords match. Login successful!
}

// Provide the pepper if you used one.
// (This is because the pepper isn't stored with the hash!)
if(Hash.password(password).pepper(pepper).verify(correctHash)) {
    // Passwords match. Login successful!
}

Hash Format

The hash format is seven fields separated by the colon (':') character.

algorithm:factor:hashLength:saltLength:pepper:salt:hash

Examples:

pbkdf2sha1:64000:18:24:n:LZXY631xphycV5kaJ2WY0RRDqSfwiZ6L:uOw06jt6FvimXSxEJipYYHsQ
pbkdf2sha256:64000:18:24:n:ZhxPG2klUysxywJ7NIAhFNTtEKa1U2yu:6oeoGuoQAOIKsztgIgPHTC4/
pbkdf2sha256:64000:18:24:y:8MD0yEl5DKz+8Av2L8985h63BhvVppYU:osTwsDh2qo/wgE6g0BrjdeFt
pbkdf2sha512:64000:18:24:n:EbroMczUKuBRx5sy+hgFQyHmqk2iNtt5:Ml8pGxc3pYoh1z5fkk5rfjM9
pbkdf2sha512:64000:18:24:y:v+tqRNA5B4cAxbZ4aUId/hvrR+FlS1d8:/R851fqvd7HItsSr0vJEupBf
bcrypt:13:66:16:n::$2a$10$YQ9urAM3RKuDtl1XaF99HrdpoIlB6ZhfaGR1T4yS4jlfMSPyeXehE.0Dway
bcrypt:13:66:16:y::$2a$10$sdreyOHQW0XAGw.LMXbPyayMMGlMuU69htdw8KXjzk5xOrVTFj2aYLxre7y
scrypt:131072:24:80:n::$s0$e0801$Evw8WPqcEUy1n3PhZcP9pg==$lRbNPFoOdoBMFT0XUcZUPvIxCY8w+9DkUklXIqCOHks=
scrypt:131072:24:80:y::$s0$e0801$mzUhOD/ns1JCnwhsYPvIkg==$OlipMfOQJkCm62kY1m79AgIsfPzmIDdgz/fl/68EQ+Y=
  • algorithm is the name of the cryptographic hash function.
  • factor parameter for the function. PBKDF2 number of iterations (64000), BCRYPT number of logrounds (212), SCRYPT cpu/mem cost (131072).
  • hashLength is the byte length of the hash.
  • saltLength is the byte length of the salt.
  • pepper is an indicator that a pepper was used ("y" or "n"). Peppers aren't stored with the Hashes. They're stored in the application properties.
  • salt is the salt. (Note: BCRYPT and SCRYPT salt is embedded in the hash).
  • hash is the raw password hash.

Options and Considerations

PBKDF2 Options

You have three options with PBKDF2 hmac: SHA1, SHA256, or SHA512. Test each before you try them, because not all JVM's support the newer hashing methods. Java 8 added support for PBKDF2 with SHA512 in 2014.

The default iterations = 64,000 but feel free to increase up to 200,000 depending on your server and cpu cost you want. Run some preliminary tests to find out if your server/device can handle the high number of iterations first. There are lots of applications out there that use anywhere from 1,000 to 10k, or 200k, for their storage.

BCrypt Options

The default logrounds = 13 but feel free to increase up to 20 depending on the cpu cost you want. Again, run some preliminary tests to find out if hashes are too quick. You'll want at least 0.5 seconds per hash and no faster. Here is a quick estimate:

  • 12 = About ~250 ms each hash.
  • 13 = About ~500 ms each hash. ๐Ÿ”‘ default
  • 14 = About ~1 second each hash.
  • 15 = About ~2 seconds each hash.
  • 16 = About ~4.5 seconds each hash.

Also note that BCrypt has a password limit of 72 characters (18 32-bit words). Be sure to truncate before hashing. Its a limitiation of the Blowfish cipher. BCrypt has a default salt length of 16 to remain compatible with the standard formula, but you can increase this if you wish.

SCrypt Options

The default cost = 131072 (217) but you can increase this too. Again, run some preliminary tests to find out if the hashes are computed too quickly. You'll want at least 0.5 seconds per hash and no faster. Here is a quick estimate:

  • 16384 (215) = About ~100 ms each hash.
  • 131072 (217) = About ~800 ms each hash ๐Ÿ”‘ default
  • 262144 (218) = About ~2 seconds each hash.
  • 1048576 (220) = About ~5 seconds each hash.

Details

By default, if you just call Hash.password(pwd).create() it uses PBKDF2 hmac SHA1 with 24 bytes (192 bits) of securely random salt and outputs 18 bytes (144 bits). 144 bits was chosen because it is (1) Less than SHA1's 160-bit output (to avoid unnecessary PBKDF2 overhead), and (2) A multiple of 6 bits, so that the base64 encoding is optimal. PBKDF2 hmac SHA1 was chosen for the default mainly for the most compatibility across Java implementations. Although SHA1 has been cryptographically broken as a collision-resistant function, it is still perfectly safe for password storage with PBKDF2. Its my recommendation though to use algorithms like BCRYPT and SCRYPT. As they are 'memory hard', meaning that they don't just need a lot of CPU power to compute, they also require a lot of memory (unlike PBKDF2). This makes them better against brute-force attacks.

Contribute

A project by Austin Delamar based off of Taylor Hornby, Damien Miller, and Will Grozer's work and other contributors.

If you'd like to contribute, feel free to fork and make changes, then open a pull request to master branch.

License

JHash is licensed as Apache-2.0

PBKDF2 is licensed as BSD-2-Clause

BCRYPT is licensed as ISC

SCRYPT is licensed as Apache-2.0

jhash's People

Contributors

amdelamar avatar asmith3006 avatar defuse avatar mattnidz avatar paragonie-scott avatar redragonx avatar sarciszewski avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

jhash's Issues

Add to bintray jcenter

First you'll need to generate a pom.xml

Then update maven/gradle instructions:

Gradle:

repositories {  
   jcenter()  
}

Maven:

<repositories>
    <repository>
        <id>bintray</id>
        <url>http://jcenter.bintray.com</url>
    </repository>
</repositories>

API is unfriendly due to overuse of checked exceptions.

https://github.com/amdelamar/jhash/blob/master/src/main/java/com/amdelamar/jhash/Hash.java#L72

https://github.com/amdelamar/jhash/blob/master/src/main/java/com/amdelamar/jhash/Hash.java#L53

Seems like bad API design to throw a (checked!) exception in the signature that can't actually happen unless your implementation is corrupt. You might as well throw an InternalError. Catch and rethrow as runtime exception (because it can't happen). Corrupt / weirdly configured JDKs might not have the appropriate algorithms in their built in security library, but a checked exception is still a bad design decision here: A user of a library trying to go with your defaults will not be able to meaningfully recover from such an error, so what are they going to put in their catch block?

https://github.com/amdelamar/jhash/blob/master/src/main/java/com/amdelamar/jhash/exception/InvalidHashException.java#L3

This should be a runtimeexception. Comes down to the same problem: If I were to write code that uses this library, and this exception occurs, what am I going to do? 99.95% of the time, what I want to do is blow the current execution stack with an exception explaining what exotic bad thing just happened. Passing on the very exception YOU throw (because the library is the best at generating a message, presumably) accomplishes just that, but being checked makes that hard for me. Sure, it's theoretically possible a corrupt hash is an expectable condition that can be recovered from, but that's a really exotic use case. Think about it: There are really only 4 possible scenarios here:

  • Your own library has a bug (such as what happens with passing in PBKDF2Gobbledygook as algorithm, then InvalidHashEx would happen). There's nothing sane I can do here. You can't use checked exceptions to make calling code somehow deal with your bugs.
  • There's a version mismatch and your library changed the scheme of that hash between versions. Similarly, nothing sane I can do but 'log it and exit', which just propagating that exception will do in a far superior way than forcing every caller to try to do that.
  • The JVM is corrupt. In which case all bets are off anyway.
  • The data in the DB got overwritten or corrupted. In which case all bets are off anyway.

Checked exceptions are for somewhat expectable conditions that one assumes can be recovered from. Let me put it this way: InternalError isn't checked either.

Question: Compatibility with jeremyh version of BCrypt

Compatibility with https://github.com/jeremyh/jBCrypt

It looks like the generated hashes are not compatible
to jeremyh version of BCrypt

String yourHash = BCrypt.create("password", 12);
String theirHash = BCrypt.hashpw("password", BCrypt.gensalt (12));

Assert.assertTrue(BCrypt.checkpw("password", yourHash)) -> FALSE

String testHash = BCrypt.create("password", theirHash, 12);
Assert.assertTrue (HashUtils.slowEquals (theirHash.getBytes(), testHash.getBytes())) -> FALSE

Where is my mistake?

Edit
BCrypt Tester: https://www.dailycred.com/article/bcrypt-calculator
theirHash -> Password and hash match
yourHash -> Password and hash do NOT match

Support bcrypt revision 2b

I'm migrating from a python backend to the jvm and would love to use this library. Most of my hashes however are currently using the 2b revision of bcrypt. This version bump technically only applied to the openBSD implementation and should be functionally identical with 2a everywhere else. It should be possible to handle it just like 2y.

For now I will just replace the $2b with $2a in all my fields as a workaround.

Version badges

Bintray
Javadoc

[![Bintray](https://img.shields.io/bintray/v/amdelamar/mvn/jhash)](https://bintray.com/amdelamar/mvn/jhash/_latestVersion)

[![Javadoc](https://www.javadoc.io/badge/com.amdelamar/jhash.svg)](https://www.javadoc.io/doc/com.amdelamar/jhash)

Missing salt rounds with BCrypt

When using BCrypt with iterations less than 10 (if not 2 chars):
Hash.password(pass).factor(9).algorithm(Type.BCRYPT).create();
However BCrypt supports 4+ rounds. I think we must add 0 before rounds if it's less then 10 when appending to hash string.

Exception in thread "main" java.lang.IllegalArgumentException: Missing salt rounds
	at com.amdelamar.jhash.algorithms.BCrypt.create(BCrypt.java:508)
	at com.amdelamar.jhash.Hash.create(Hash.java:196)
	at ua.i0xhex.discr.Main.main(Main.java:16)

char[] to String conversion is breaking admittedly overblown security principle.

https://github.com/amdelamar/jhash/blob/master/src/main/java/com/amdelamar/jhash/Hash.java#L265

This is a security issue โ€“ the reason you sometimes see passwords/salts passed as char arrays, and not Strings, is that Strings are impossible to securely eliminate from the process; they don't disappear until they get GCed and might even end up interned someplace. You can't wipe their contents either. If you eliminate the content of a char array (Arrays.fill(charArray, '\0')), you've got a better chance of eliminating the content from memory introspection tools. This is a pretty exotic security measure (if some process gets to look into your JVM process internals, you've got problems!), but nevertheless, that is why things like JPasswordField and such work with char[]. By having char[] based arguments in the first place you're creating the illusion that you're supportive of this security protocol, but you're not, as you're making strings out of em. Either eliminate the char[] based methods, OR document with all sorts of <blink> tags and other such 'HERE BE DRAGONS!' (you can tell I'm not a fan of this plan) that you're writing a check your code can't cash, OR BOTH stop turning them into strings, wipe all the char[] arrays you make yourself, and either wipe the input param char array or document that caller should take care of that ASAP. Do a global search for String to find all cases of this, same for char[].

Re-license to Apache 2.0

Re-license just the "Jhash" portion. Not the individual algorithms. Although, it would be nice to relicense each of them to be the same license, but we'll save that for a later time.

Rename package to com.amdelamar

Currently com.github.amdelamar does not match the actual package com.amdelamar used.
This should be updated so it can be published to maven central.

Crash on API < 26

Hi,

I have a runtime crash on devices with API < 26

java.lang.NoClassDefFoundError: Failed resolution of: Ljava/util/Base64;
        at com.amdelamar.jhash.util.HashUtils.encodeBase64(HashUtils.java:103)
        at com.amdelamar.jhash.Hash.create(Hash.java:182)

This class seems to be not available on older devices.

Travis builds can be faster

Skip the install step like so:

before_install:
 - chmod +x gradlew

install: echo "skipping install step"

script:
 - "./gradlew check"

The default install step runs gradle assemble. So doing that AND gradle check isn't really useful when tasks are being repeated on every build.

Consider using the builder pattern for Hash.create.

https://github.com/amdelamar/jhash/blob/master/src/main/java/com/amdelamar/jhash/Hash.java#L180

You've got 10 methods called 'create' here, and some of the paramtypes are the same. This is going to cause confusion and it's unclear what/how to use this. My advice:

Have 1 convenience method that just takes a single String, and then have a builder kind of deal. Instead of:

Hash.create("pass", "pepper", 13), I'd write: Hash.create().password("pass").pepper("pepper").complexityFactor(13).build().

parameter is not clear enough as to the intent of that variable. The named methods ensure everyone can at a glance see what's going on, IDE autocomplete will guide you as to what you can do and lets you write javadoc for each element, and while that's more code to write, you don't actually specify pepper, rounds, algorithm, etc, unless it's important to do so (you'd go with the default choices if it's not), so apparently these things are important, which means: It's good that they are all explicitly named like this.

Note, if I were to write:

Hash.create("pepper", "pass", 13), the code compiles and runs without warnings or errors, and yet that doesn't do what I expected (I flipped the pepper and pass args). There's no way I can easily tell I messed that up. Had I written: Hash.of().password("pepper").pepper("password").create(), I can visually identify I'm doing it wrong, and it's highly unlikely I'd ever make that mistake in the first place.

Define custom salt value

Hi,

My project have the below requirement

  1. hash algorithm to use is Password-based Key Derivation Function 2 (PBKDF2).
  2. The salt values must be at least 32 bit in length and unique for every password entry
  3. The salt values must be generated using an approved cryptographically secure random number generator (CSRNG).

For the 1st and 2nd point, I think the jhash can achieve. But for the 3rd point, is there any way for jhash library to allow us to specify our own custom salt values, rather than let the jhash library generate. The intention is to allow my application to use an approved CSRNG to generate the salt values.

Fix javadocs

See all the issues by running:

./gradlew javadoc

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.