Skip to content

Commit

Permalink
Add commons-codec Base64 #8 #24
Browse files Browse the repository at this point in the history
  • Loading branch information
amdelamar committed May 11, 2019
1 parent bc55baa commit 411e47c
Show file tree
Hide file tree
Showing 5 changed files with 75 additions and 77 deletions.
1 change: 1 addition & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ repositories {
}

dependencies {
implementation 'commons-codec:commons-codec:1.8'
testCompile 'junit:junit:4.12'
}

Expand Down
27 changes: 13 additions & 14 deletions src/main/java/com/amdelamar/jhash/Hash.java
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,10 @@
* check if valid password hash matches.
*
* @author amdelamar
* @version 2.0.0
* @since 1.0.0
* @version 2.2.0
* @see <a href="https://github.com/amdelamar/jhash">https://github.com/amdelamar/jhash</a>
* @see <a href="https://en.wikipedia.org/wiki/Hash_function">https://en.wikipedia.org/wiki/Hash_function</a>
* @since 1.0.0
*/
public class Hash {

Expand Down Expand Up @@ -49,6 +49,7 @@ public class Hash {
* The password to be hashed. Note: Call create() when ready to output the hash value.
* You can also specify optional parameters such as pepper, factor, algorithm, and more.
* But this has to be done before you call create().
*
* @param password char[]
* @return Hash
* @throws IllegalArgumentException if password is null or empty
Expand All @@ -64,6 +65,7 @@ public static Hash password(char[] password) {

/**
* Optional value for the application-specific <a href="https://en.wikipedia.org/wiki/Pepper_(cryptography)">pepper</a>.
*
* @param pepper char[]
* @return Hash
* @see <a href="https://en.wikipedia.org/wiki/Pepper_(cryptography)">https://en.wikipedia.org/wiki/Pepper_(cryptography)</a>
Expand All @@ -75,6 +77,7 @@ public Hash pepper(char[] pepper) {

/**
* Optional value for hash byte length. Default is 18.
*
* @param hashLength int
* @return Hash
*/
Expand All @@ -85,6 +88,7 @@ public Hash hashLength(int hashLength) {

/**
* Optional value for salt byte length. Default is 24.
*
* @param saltLength int
* @return Hash
*/
Expand All @@ -97,6 +101,7 @@ public Hash saltLength(int saltLength) {
* Optional value for selecting hash algorithm. E.g. Type.PBKDF2_SHA512, Type.BCRYPT,
* or Type.SCRYPT.
* Default is Type.PBKDF2_SHA1
*
* @param algorithm Type
* @return Hash
*/
Expand All @@ -108,6 +113,7 @@ public Hash algorithm(Type algorithm) {
/**
* Optional value for iterations (PBKDF2), logrounds (BCRYPT), or cost (SCRYPT). Set to 0
* if you're unsure and it will use the default value for the specified algorithm.
*
* @param factor int
* @return Hash
*/
Expand All @@ -119,9 +125,9 @@ public Hash factor(int factor) {
/**
* Creates a Hash from the given char array using the specified algorithm. Use this to
* create new user's passwords. Or when they change their password.
*
* @return String hash
* @throws IllegalArgumentException
* if one or more parameters are invalid
* @throws IllegalArgumentException if one or more parameters are invalid
* @see <a href="https://en.wikipedia.org/wiki/Hash_function">https://en.wikipedia.org/wiki/Hash_function</a>
*/
public String create() throws IllegalArgumentException {
Expand Down Expand Up @@ -255,11 +261,9 @@ public String create() throws IllegalArgumentException {
* Use this to verify a user login. Note: you must provide a pepper before calling this method
* if you used a pepper to hash the correctHash from before.
*
* @param correctHash
* The string hash from storage.
* @param correctHash The string hash from storage.
* @return boolean true if matches
* @throws InvalidHashException
* if the correctHash was missing parts or invalid
* @throws InvalidHashException if the correctHash was missing parts or invalid
* @see <a href="https://en.wikipedia.org/wiki/Hash_function">https://en.wikipedia.org/wiki/Hash_function</a>
*/
public boolean verify(String correctHash) throws InvalidHashException {
Expand Down Expand Up @@ -291,12 +295,7 @@ public boolean verify(String correctHash) throws InvalidHashException {
pepperPassword = (new String(pepper) + pepperPassword);
}

byte[] salt = null;
try {
salt = HashUtils.decodeBase64(params[SALT_INDEX]);
} catch (IllegalArgumentException ex) {
throw new InvalidHashException("Base64 decoding of salt failed.", ex);
}
byte[] salt = HashUtils.decodeBase64(params[SALT_INDEX]);

int storedHashSize = 0;
try {
Expand Down
56 changes: 24 additions & 32 deletions src/main/java/com/amdelamar/jhash/util/HashUtils.java
Original file line number Diff line number Diff line change
@@ -1,23 +1,25 @@
package com.amdelamar.jhash.util;

import org.apache.commons.codec.binary.Base64;
import org.apache.commons.codec.binary.StringUtils;

import java.security.SecureRandom;
import java.util.Base64;

/**
* Hash Utility and Common functions.
*
*
* @author amdelamar
* @since 1.0.0
*/
public final class HashUtils {

private HashUtils() {
// prevent instantiation
}

/**
* Generates a secure random salt of 24 bytes.
*
*
* @return byte array salt
*/
public static byte[] randomSalt() {
Expand All @@ -26,9 +28,8 @@ public static byte[] randomSalt() {

/**
* Generates a secure random salt of the specified size.
*
* @param size
* The size of the salt in bytes.
*
* @param size The size of the salt in bytes.
* @return byte array salt
*/
public static byte[] randomSalt(int size) {
Expand All @@ -37,9 +38,8 @@ public static byte[] randomSalt(int size) {

/**
* Generates a secure random salt of 24 bytes.
*
* @param secureRandom
* SecureRandom thats hopefully seeded
*
* @param secureRandom SecureRandom thats hopefully seeded
* @return byte array salt
*/
public static byte[] randomSalt(SecureRandom secureRandom) {
Expand All @@ -48,11 +48,9 @@ public static byte[] randomSalt(SecureRandom secureRandom) {

/**
* Generates a secure random salt of the specified size.
*
* @param secureRandom
* SecureRandom thats hopefully seeded
* @param size
* The size of the salt in bytes.
*
* @param secureRandom SecureRandom thats hopefully seeded
* @param size The size of the salt in bytes.
* @return byte array salt
*/
public static byte[] randomSalt(SecureRandom secureRandom, int size) {
Expand All @@ -63,11 +61,9 @@ public static byte[] randomSalt(SecureRandom secureRandom, int size) {

/**
* Compares two byte arrays.
*
* @param byteA
* First byte array.
* @param byteB
* Second byte array.
*
* @param byteA First byte array.
* @param byteB Second byte array.
* @return true if they are equivalent.
*/
public static boolean slowEquals(byte[] byteA, byte[] byteB) {
Expand All @@ -79,29 +75,25 @@ public static boolean slowEquals(byte[] byteA, byte[] byteB) {
}

/**
* Decodes a Base64 string to a byte array. A convenience method for java.util.Base64 decoder.
*
* @param string
* (in Base64)
* Decodes a Base64 string to a byte array. A convenience method for Base64 decoder.
*
* @param string (in Base64)
* @return Base64 decoded byte array
* @see <a href="https://en.wikipedia.org/wiki/Base64">https://en.wikipedia.org/wiki/Base64</a>
*/
public static byte[] decodeBase64(String string) {
return Base64.getDecoder()
.decode(string);
return Base64.decodeBase64(string);
}

/**
* Encodes a byte array into a Base64 string. A convenience method for java.util.Base64 encoder.
*
* @param array
* (byte array)
* Encodes a byte array into a Base64 string. A convenience method for Base64 encoder.
*
* @param array (byte array)
* @return Base64 encoded string
* @see <a href="https://en.wikipedia.org/wiki/Base64">https://en.wikipedia.org/wiki/Base64</a>
*/
public static String encodeBase64(byte[] array) {
return new String(Base64.getEncoder()
.encode(array));
return StringUtils.newStringUtf8(Base64.encodeBase64(array));
}

}
28 changes: 12 additions & 16 deletions src/test/java/com/amdelamar/jhash/HashTests.java
Original file line number Diff line number Diff line change
@@ -1,20 +1,16 @@
package com.amdelamar.jhash;

import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;

import com.amdelamar.jhash.algorithms.Type;
import com.amdelamar.jhash.exception.InvalidHashException;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;

import com.amdelamar.jhash.algorithms.Type;
import com.amdelamar.jhash.exception.InvalidHashException;
import static org.junit.Assert.*;

@RunWith(JUnit4.class)
public class HashTests {

@Test
public void constructorTests() {
Hash hash = new Hash();
Expand Down Expand Up @@ -133,7 +129,7 @@ public void invalidAlgorithmTests() {
} catch (IllegalArgumentException | InvalidHashException e) {
// good catch
}

try {
// bad algorithm name
Hash.password(password)
Expand Down Expand Up @@ -234,7 +230,7 @@ public void nullHashTests() {
}
assertTrue(caught);
}

@Test
public void invalidHashTests() {
char[] password = "HelloWorld".toCharArray();
Expand All @@ -247,7 +243,7 @@ public void invalidHashTests() {
} catch (Exception e) {
// good error
}

try {
// zero iterations
Hash.password(password)
Expand All @@ -265,16 +261,16 @@ public void invalidHashTests() {
} catch (Exception e) {
// good error
}

try {
// bad salt encoding
Hash.password(password)
.verify("pbkdf2sha1:64000:18:24:n:~LZXY631xphycV5kaJ2WY0RRDqSfwiZ6L:uOw06jt6FvimXSxEJipYYHsQ");
.verify("pbkdf2sha1:64000:18:24:n::uOw06jt6FvimXSxEJipYYHsQ");
fail("bad salt encoding not detected");
} catch (Exception e) {
// good error
}

try {
// bad hash size
Hash.password(password)
Expand All @@ -283,7 +279,7 @@ public void invalidHashTests() {
} catch (Exception e) {
// good error
}

try {
// bad hash
Hash.password(password)
Expand All @@ -292,7 +288,7 @@ public void invalidHashTests() {
} catch (Exception e) {
// good error
}

try {
// bad salt size
Hash.password(password)
Expand Down
40 changes: 25 additions & 15 deletions src/test/java/com/amdelamar/jhash/util/HashUtilsTests.java
Original file line number Diff line number Diff line change
@@ -1,23 +1,17 @@
package com.amdelamar.jhash.util;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;

import java.security.SecureRandom;

import com.amdelamar.jhash.exception.InvalidHashException;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;

import com.amdelamar.jhash.exception.InvalidHashException;
import com.amdelamar.jhash.util.HashUtils;
import java.security.SecureRandom;

import static org.junit.Assert.*;

@RunWith(JUnit4.class)
public class HashUtilsTests {

@Test
public void saltTests() {

Expand Down Expand Up @@ -54,7 +48,7 @@ public void slowEqualTests() {
boolean slow2 = HashUtils.slowEquals(HashUtils.encodeBase64(hello.getBytes())
.getBytes(), hello64.getBytes());
assertTrue(slow2);

boolean slow3 = HashUtils.slowEquals(hello64.getBytes(), hello.getBytes());
assertFalse(slow3);
}
Expand All @@ -67,14 +61,30 @@ public void base64Tests() {

assertEquals(hello64, HashUtils.encodeBase64(hello.getBytes()));
assertEquals(hello, new String(HashUtils.decodeBase64(hello64)));

String loremispum = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt " +
"ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris " +
"nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit " +
"esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in " +
"culpa qui officia deserunt mollit anim id est laborum.";
String loremispum64 = "TG9yZW0gaXBzdW0gZG9sb3Igc2l0IGFtZXQsIGNvbnNlY3RldHVyIGFkaXBpc2NpbmcgZWxpdCwgc2VkIGRvIGV" +
"pdXNtb2QgdGVtcG9yIGluY2lkaWR1bnQgdXQgbGFib3JlIGV0IGRvbG9yZSBtYWduYSBhbGlxdWEuIFV0IGVuaW0gYWQgbWluaW0g" +
"dmVuaWFtLCBxdWlzIG5vc3RydWQgZXhlcmNpdGF0aW9uIHVsbGFtY28gbGFib3JpcyBuaXNpIHV0IGFsaXF1aXAgZXggZWEgY29tb" +
"W9kbyBjb25zZXF1YXQuIER1aXMgYXV0ZSBpcnVyZSBkb2xvciBpbiByZXByZWhlbmRlcml0IGluIHZvbHVwdGF0ZSB2ZWxpdCBlc3" +
"NlIGNpbGx1bSBkb2xvcmUgZXUgZnVnaWF0IG51bGxhIHBhcmlhdHVyLiBFeGNlcHRldXIgc2ludCBvY2NhZWNhdCBjdXBpZGF0YXQ" +
"gbm9uIHByb2lkZW50LCBzdW50IGluIGN1bHBhIHF1aSBvZmZpY2lhIGRlc2VydW50IG1vbGxpdCBhbmltIGlkIGVzdCBsYWJvcnVt" +
"Lg==";

assertEquals(loremispum64, HashUtils.encodeBase64(loremispum.getBytes()));
assertEquals(loremispum, new String(HashUtils.decodeBase64(loremispum64)));
}

@Test
public void invalidHashExceptionTests() {

InvalidHashException ex = new InvalidHashException("Invalid");
InvalidHashException ex2 = new InvalidHashException("Invalid2", new Throwable());

assertEquals("Invalid", ex.getMessage());
assertEquals("Invalid2", ex2.getMessage());
}
Expand Down

0 comments on commit 411e47c

Please sign in to comment.