Skip to content

Commit c008240

Browse files
committed
Initial Commit
0 parents  commit c008240

28 files changed

Lines changed: 749 additions & 0 deletions

File tree

.classpath

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<classpath>
3+
<classpathentry kind="src" output="target/classes" path="src/main/java">
4+
<attributes>
5+
<attribute name="optional" value="true"/>
6+
<attribute name="maven.pomderived" value="true"/>
7+
</attributes>
8+
</classpathentry>
9+
<classpathentry kind="src" output="target/test-classes" path="src/test/java">
10+
<attributes>
11+
<attribute name="optional" value="true"/>
12+
<attribute name="maven.pomderived" value="true"/>
13+
<attribute name="test" value="true"/>
14+
</attributes>
15+
</classpathentry>
16+
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/J2SE-1.5">
17+
<attributes>
18+
<attribute name="maven.pomderived" value="true"/>
19+
</attributes>
20+
</classpathentry>
21+
<classpathentry kind="con" path="org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER">
22+
<attributes>
23+
<attribute name="maven.pomderived" value="true"/>
24+
</attributes>
25+
</classpathentry>
26+
<classpathentry kind="output" path="target/classes"/>
27+
</classpath>

.project

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<projectDescription>
3+
<name>securehash</name>
4+
<comment></comment>
5+
<projects>
6+
</projects>
7+
<buildSpec>
8+
<buildCommand>
9+
<name>org.eclipse.jdt.core.javabuilder</name>
10+
<arguments>
11+
</arguments>
12+
</buildCommand>
13+
<buildCommand>
14+
<name>org.eclipse.m2e.core.maven2Builder</name>
15+
<arguments>
16+
</arguments>
17+
</buildCommand>
18+
</buildSpec>
19+
<natures>
20+
<nature>org.eclipse.jdt.core.javanature</nature>
21+
<nature>org.eclipse.m2e.core.maven2Nature</nature>
22+
</natures>
23+
</projectDescription>
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
eclipse.preferences.version=1
2+
encoding//src/main/java=UTF-8
3+
encoding//src/test/java=UTF-8
4+
encoding/<project>=UTF-8
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
eclipse.preferences.version=1
2+
org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
activeProfiles=
2+
eclipse.preferences.version=1
3+
resolveWorkspaceProjects=true
4+
version=1

pom.xml

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
<project xmlns="http://maven.apache.org/POM/4.0.0"
2+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
3+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
4+
<modelVersion>4.0.0</modelVersion>
5+
6+
<groupId>io.pointgenesis.utilities</groupId>
7+
<artifactId>securehash</artifactId>
8+
<version>1.0.0.1</version>
9+
<packaging>jar</packaging>
10+
11+
<name>securehash</name>
12+
<url>http://maven.apache.org</url>
13+
14+
<properties>
15+
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
16+
<maven.compiler.target>1.8</maven.compiler.target>
17+
<maven.compiler.source>1.8</maven.compiler.source>
18+
<junit.version>4.12</junit.version>
19+
<log4j2.version>2.12.1</log4j2.version>
20+
<jaxb.api.version>2.3.1</jaxb.api.version>
21+
</properties>
22+
23+
<dependencies>
24+
<dependency>
25+
<groupId>junit</groupId>
26+
<artifactId>junit</artifactId>
27+
<version>${junit.version}</version>
28+
<scope>test</scope>
29+
</dependency>
30+
<!-- https://mvnrepository.com/artifact/org.apache.logging.log4j/log4j-core -->
31+
<dependency>
32+
<groupId>org.apache.logging.log4j</groupId>
33+
<artifactId>log4j-core</artifactId>
34+
<version>${log4j2.version}</version>
35+
</dependency>
36+
<!-- https://mvnrepository.com/artifact/org.apache.logging.log4j/log4j-api -->
37+
<dependency>
38+
<groupId>org.apache.logging.log4j</groupId>
39+
<artifactId>log4j-api</artifactId>
40+
<version>${log4j2.version}</version>
41+
</dependency>
42+
<!-- https://mvnrepository.com/artifact/javax.xml.bind/jaxb-api -->
43+
<dependency>
44+
<groupId>javax.xml.bind</groupId>
45+
<artifactId>jaxb-api</artifactId>
46+
<version>${jaxb.api.version}</version>
47+
</dependency>
48+
</dependencies>
49+
</project>
Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
package io.pointgenesis.utilities.security.hash;
2+
3+
import java.nio.charset.StandardCharsets;
4+
import java.security.MessageDigest;
5+
import java.security.NoSuchAlgorithmException;
6+
7+
import javax.xml.bind.DatatypeConverter;
8+
9+
import org.apache.logging.log4j.LogManager;
10+
import org.apache.logging.log4j.Logger;
11+
12+
/**
13+
* A one-way hash generator for the SHA-2 family of algorithms. The preferred hash mechanism is
14+
* shown in SecureHash.java. The fast hash methodology is provided for comparison only.
15+
*
16+
* <ul>
17+
* <li>SHA-224</li>
18+
* <li>SHA-256</li>
19+
* <li>SHA-384</li>
20+
* <li>SHA-512</li>
21+
* <li>SHA-512/224</li>
22+
* <li>SHA-512/256</li>
23+
* </ul>
24+
*
25+
* References:
26+
* [1] https://en.wikipedia.org/wiki/Secure_Hash_Algorithms
27+
* [2] https://www.mkyong.com/java/java-sha-hashing-example
28+
* [3] https://stackoverflow.com/questions/33085493/hash-a-password-with-sha-512-in-java
29+
*
30+
* @author Travis L. Steinmetz
31+
*
32+
* Copyright 2019 Point Genesis Solutions, LLC
33+
*
34+
* Licensed under the Apache License, Version 2.0 (the "License");
35+
* you may not use this file except in compliance with the License.
36+
* You may obtain a copy of the License at
37+
*
38+
* http://www.apache.org/licenses/LICENSE-2.0
39+
*
40+
* Unless required by applicable law or agreed to in writing, software
41+
* distributed under the License is distributed on an "AS IS" BASIS,
42+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
43+
* See the License for the specific language governing permissions and
44+
* limitations under the License.
45+
*/
46+
public class FastHash {
47+
private static final Logger log = LogManager.getLogger();
48+
49+
/** Marked private to prevent unwanted instantiation. **/
50+
private FastHash() {
51+
}
52+
53+
/**
54+
* Generates a one-way hash from the given parameters.
55+
*
56+
* @param value the clear-text value to hash.
57+
* @param salt the salt to apply while generating the hash.
58+
*
59+
* @return the hashed representation of {@code}value + "." + {@code}salt.
60+
*/
61+
public static String getHash(final String value, final String salt) {
62+
String DEFAULT_HASH_ALGORITHM = "SHA-512";
63+
return getHash(value, salt, DEFAULT_HASH_ALGORITHM);
64+
}
65+
66+
/**
67+
* Generates a one-way hash from the given parameters.
68+
*
69+
* @param value the clear-text value to hash.
70+
* @param salt the salt to apply while generating the hash.
71+
* @param algorithm the SHA-2 algorithm that will be used to generate the hash.
72+
*
73+
* @return the hashed representation of {@code}value + "." + {@code}salt.
74+
*/
75+
public static String getHash(final String value, final String salt, final String algorithm) {
76+
byte[] bytes = null;
77+
78+
try {
79+
MessageDigest md = MessageDigest.getInstance(algorithm);
80+
md.update(DatatypeConverter.parseHexBinary(salt));
81+
82+
bytes = md.digest(value.getBytes(StandardCharsets.UTF_8));
83+
log.debug("hashed value: {}", DatatypeConverter.printHexBinary(bytes));
84+
} catch (NoSuchAlgorithmException e) {
85+
String valueAsHex = null;
86+
if (value != null) {
87+
valueAsHex = DatatypeConverter.printHexBinary(value.getBytes(StandardCharsets.UTF_8));
88+
}
89+
log.error("Error encountered while generating hash. value: {} | salt: {} | algorithm: {}", valueAsHex, salt, algorithm);
90+
throw new IllegalArgumentException("Error encountered while generating hash");
91+
}
92+
93+
return DatatypeConverter.printHexBinary(bytes) + "." + salt;
94+
}
95+
96+
/**
97+
* Compares a previously hashed value to another value that is hashed with the same
98+
* salt value as the previously hashed value. If the resulting hash values are equal,
99+
* then the provided input matches the previously pre-hashed value.
100+
*
101+
* @param rawValue the plain-text value to hash and compare against a previously hashed value.
102+
* @param hashedValueAndSalt the previously hashed value and the salt used in the hash operation.
103+
*
104+
* @return true if the hashed values are equal, otherwise false.
105+
*/
106+
public static boolean compare(String rawValue, String hashedValueAndSalt) {
107+
String[] values = hashedValueAndSalt.split("\\.");
108+
109+
if (values.length != 2) {
110+
log.error("Expected exactly two tokens. But found {} tokens in hashedValueAndSalt: {}.", values.length, hashedValueAndSalt);
111+
throw new IllegalArgumentException("Incorrect number of tokens found in \"hashedValueAndSalt\" value.");
112+
}
113+
114+
String hashedValue = values[0];
115+
String saltValue = values[1];
116+
117+
String generatedHashAndSalt = getHash(rawValue, saltValue);
118+
119+
String[] generatedValues = generatedHashAndSalt.split("\\.");
120+
String generatedHash = generatedValues[0];
121+
122+
log.debug("Generated hash: {}", generatedHash);
123+
124+
return hashedValue.contentEquals(generatedHash);
125+
}
126+
}
Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
package io.pointgenesis.utilities.security.hash;
2+
3+
import java.security.NoSuchAlgorithmException;
4+
import java.security.spec.InvalidKeySpecException;
5+
6+
import javax.crypto.SecretKey;
7+
import javax.crypto.SecretKeyFactory;
8+
import javax.crypto.spec.PBEKeySpec;
9+
import javax.xml.bind.DatatypeConverter;
10+
11+
import org.apache.logging.log4j.LogManager;
12+
import org.apache.logging.log4j.Logger;
13+
14+
/**
15+
* Generates a hash following the OWASP recommended practices for password hashing.
16+
*
17+
* References:
18+
*
19+
* [1] https://www.owasp.org/index.php/Hashing_Java
20+
*
21+
* @author Travis L. Steinmetz
22+
*
23+
* Copyright 2019 Point Genesis Solutions, LLC
24+
*
25+
* Licensed under the Apache License, Version 2.0 (the "License");
26+
* you may not use this file except in compliance with the License.
27+
* You may obtain a copy of the License at
28+
*
29+
* http://www.apache.org/licenses/LICENSE-2.0
30+
*
31+
* Unless required by applicable law or agreed to in writing, software
32+
* distributed under the License is distributed on an "AS IS" BASIS,
33+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
34+
* See the License for the specific language governing permissions and
35+
* limitations under the License.
36+
*/
37+
38+
public class SecureHash {
39+
private static final Logger log = LogManager.getLogger();
40+
41+
/** Marked private to prevent outside instantiation. **/
42+
private SecureHash() {
43+
}
44+
45+
/**
46+
* Generates a one-way hash from the given inputs.
47+
*
48+
* @param value the clear text value to transform.
49+
* @param salt the salt value to apply while hashing {@code}value.
50+
*
51+
* @return the hashed representation of {@code}value.
52+
*/
53+
public static String getHash(final String value, final String salt) {
54+
int DEFAULT_ITERATIONS = 65536;
55+
return getHash(value, salt, DEFAULT_ITERATIONS);
56+
}
57+
58+
/**
59+
* Generates a one-way hash from the given inputs.
60+
*
61+
* @param value the clear text value to transform.
62+
* @param salt the salt value to apply while hashing {@code}value.
63+
* @param iterations the number of hash iterations to execute against {@code}value.
64+
*
65+
* @return the hashed representation of {@code}value.
66+
*/
67+
public static String getHash(final String value, final String salt, final int iterations) {
68+
int DEFAULT_LENGTH = 256;
69+
return getHash(value, salt, iterations, DEFAULT_LENGTH);
70+
}
71+
72+
/**
73+
* Generates a one-way hash from the given inputs.
74+
*
75+
* @param value the clear text value to transform.
76+
* @param salt the salt value to apply while hashing {@code}value.
77+
* @param iterations the number of hash iterations to execute against {@code}value.
78+
* @param length the key length.
79+
*
80+
* @return the hashed representation of {@code}value.
81+
*/
82+
public static String getHash(
83+
final String value, final String salt,
84+
final int iterations, final int length) {
85+
String DEFAULT_ALGORITHM = "PBKDF2WithHmacSHA512";
86+
return getHash(value, salt, iterations, length, DEFAULT_ALGORITHM);
87+
}
88+
89+
/**
90+
* Generates a one-way hash from the given inputs.
91+
*
92+
* @param value the clear text value to transform.
93+
* @param salt the salt value to apply while hashing {@code}value.
94+
* @param iterations the number of hash iterations to execute against {@code}value.
95+
* @param length the key length.
96+
* @param algorithm the algorithm used in the hash generation.
97+
*
98+
* @return the hashed representation of {@code}value.
99+
*/
100+
public static String getHash(
101+
final String value, final String salt,
102+
final int iterations, final int length, final String algorithm) {
103+
boolean isValidInput = true;
104+
105+
if (value == null || value.isEmpty()) {
106+
isValidInput = false;
107+
log.error("\"value\" cannot be null or empty.");
108+
}
109+
110+
if (salt == null || salt.isEmpty()) {
111+
isValidInput = false;
112+
log.error("\"salt\" cannot be null or empty.");
113+
}
114+
115+
if (algorithm == null || algorithm.isEmpty()) {
116+
isValidInput = false;
117+
log.error("\"algorithm\" cannot be null or empty.");
118+
}
119+
120+
try {
121+
if (isValidInput == false) {
122+
throw new IllegalArgumentException("One or more inputs do not conform to the expected format.");
123+
}
124+
125+
SecretKeyFactory skf = SecretKeyFactory.getInstance(algorithm);
126+
PBEKeySpec spec = new PBEKeySpec(value.toCharArray(), DatatypeConverter.parseHexBinary(salt), iterations, length);
127+
SecretKey key = skf.generateSecret(spec);
128+
129+
return DatatypeConverter.printHexBinary(key.getEncoded());
130+
} catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
131+
String valueAsString = null;
132+
if (value != null) {
133+
valueAsString = new String(value);
134+
try {
135+
/*Obfuscate the value passed into the method for security purposes.*/
136+
valueAsString = DatatypeConverter.printHexBinary(valueAsString.getBytes());
137+
} catch (Exception ex) {
138+
log.warn("Unable to convert given \"value\" to hex binary format.");
139+
}
140+
}
141+
log.error(
142+
"Unable to compute hash of the following inputs -> value: {} | salt: {} | iterations: {} | length: {} | algorithm: {}",
143+
valueAsString, salt, iterations, length, algorithm);
144+
throw new IllegalArgumentException("Unable to generate a hash using the provided inputs.", e);
145+
}
146+
}
147+
148+
}

0 commit comments

Comments
 (0)