Skip to content

Instantly share code, notes, and snippets.

@kimjj81
Forked from ToastShaman/PasswordUtils.java
Created April 30, 2018 07:16
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save kimjj81/954cb451f4ee3850490c770657eff629 to your computer and use it in GitHub Desktop.
Save kimjj81/954cb451f4ee3850490c770657eff629 to your computer and use it in GitHub Desktop.
A utility class for hashing passwords using PBKDF2 with BouncyCastle.
package com.zuhlke.lsapi;
import org.bouncycastle.crypto.PBEParametersGenerator;
import org.bouncycastle.crypto.digests.SHA3Digest;
import org.bouncycastle.crypto.generators.PKCS5S2ParametersGenerator;
import org.bouncycastle.crypto.params.KeyParameter;
import org.bouncycastle.crypto.prng.DigestRandomGenerator;
import java.util.Base64;
import static com.google.common.base.Charsets.UTF_8;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Strings.isNullOrEmpty;
import static java.lang.String.format;
public class PasswordUtils {
private static final DigestRandomGenerator generator = new DigestRandomGenerator(new SHA3Digest(512));
private PasswordUtils() {}
public static String hash(String plainPassword) {
return hash(plainPassword, salt(128), 512, 101501);
}
public static String hash(String plainPassword, byte[] salt) {
return hash(plainPassword, salt, 512, 101501);
}
public static String hash(String plainPassword, byte[] salt, int keyLength, int iterations) {
checkArgument(!isNullOrEmpty(plainPassword), "password can not be empty or null");
checkArgument(keyLength > 0, "the key length must be greater than 0");
checkArgument(iterations >= 0, "the number of iterations must be positive");
PKCS5S2ParametersGenerator generator = new PKCS5S2ParametersGenerator();
generator.init(PBEParametersGenerator.PKCS5PasswordToBytes(
plainPassword.toCharArray()),
salt,
iterations);
return format("%s|%s",
encode(salt),
encode(((KeyParameter) generator.generateDerivedParameters(keyLength)).getKey()));
}
public static boolean verify(String plainPassword, String hash) {
checkArgument(!isNullOrEmpty(plainPassword));
checkArgument(!isNullOrEmpty(hash));
return hash(plainPassword, decode(extractSalt(hash))).equals(hash);
}
private static byte[] salt(int count) {
byte[] salt = new byte[count];
generator.nextBytes(salt);
return salt;
}
private static String encode(byte[] input) {
return Base64.getEncoder().encodeToString(input);
}
private static byte[] decode(String input) {
return Base64.getDecoder().decode(input.getBytes(UTF_8));
}
private static String extractSalt(String input) {
return input.substring(0, input.indexOf("|"));
}
}
package com.zuhlke.lsapi;
import com.google.common.base.Charsets;
import org.junit.Test;
import java.util.Base64;
import static com.zuhlke.lsapi.PasswordUtils.hash;
import static com.zuhlke.lsapi.PasswordUtils.verify;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.equalTo;
public class PasswordUtilsTest {
@Test public void
hash_password_with_PKCS5() {
String password = hash("password", Base64.getEncoder().encode("HelloWorld".getBytes(Charsets.UTF_8)));
assertThat(password, equalTo("U0dWc2JHOVhiM0pzWkE9PQ==|9oIHmBVsNFeUkoajyncM928Hsqb34IhY7csx0odLr/r6/9vlZPQMmUlcYW+jv2zQKz+3h4rWu3sEOblzpi8Acg=="));
}
@Test public void
verify_password() {
boolean match = verify("password", hash("password"));
assertThat("the passwords should have matched", match, equalTo(true));
}
@Test public void
throws_exception_if_password_is_null() {
try {
hash(null);
} catch (Exception e) {
assertThat(e.getMessage(), equalTo("password can not be empty or null"));
}
}
@Test public void
throws_exception_if_password_is_empty() {
try {
hash(" ");
} catch (Exception e) {
assertThat(e.getMessage(), equalTo("password can not be empty or null"));
}
}
}
<dependencies>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15on</artifactId>
<version>1.50</version>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>16.0.1</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.3.1</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest-all</artifactId>
<version>1.3</version>
<scope>test</scope>
</dependency>
</dependencies>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment