Skip to content

Commit 012bc2a

Browse files
author
drighetto
committed
Add method computing 'safe' hash - Implement #1
1 parent f3c6674 commit 012bc2a

File tree

2 files changed

+61
-0
lines changed

2 files changed

+61
-0
lines changed

src/main/java/eu/righettod/SecurityUtils.java

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,9 @@
3232
import java.net.InetAddress;
3333
import java.net.MalformedURLException;
3434
import java.net.URL;
35+
import java.nio.charset.StandardCharsets;
3536
import java.nio.file.Files;
37+
import java.security.MessageDigest;
3638
import java.util.*;
3739
import java.util.regex.Pattern;
3840
import java.util.zip.ZipEntry;
@@ -532,4 +534,34 @@ public static boolean isPublicIPAddress(String ip) {
532534
}
533535
return isValid;
534536
}
537+
538+
/**
539+
* Compute a SHA256 hash from an input composed of a collection of strings.<br>
540+
* This method take care to build the source string in a way to prevent this source string to be prone to abuse<br>
541+
* targeting the different parts composing it.<br>
542+
* Example of abuse:<br>
543+
* <code>SHA256("Hello" + "My" + "World!!!")</code> is not equals to <code>SHA256("Hell" + "oMyW" + "orld!!!")</code>
544+
*
545+
* @param parts Ordered list of strings to use to build the input string for which the hash must be computed on. No null value is accepted on object composing the collection.
546+
* @return The hash, as an array of bytes, to allow caller to convert it to the final representation wanted (HEX, Base64, etc.). If the collection passed is null or empty then the method return null.
547+
* @throws Exception If any exception occurs
548+
* @see "https://pentesterlab.com/badges/codereview"
549+
*/
550+
public static byte[] computeHashNoProneToAbuseOnParts(List<String> parts) throws Exception {
551+
byte[] hash = null;
552+
if (parts != null && !parts.isEmpty()) {
553+
//Ensure that not part is null
554+
if (parts.stream().anyMatch(Objects::isNull)) {
555+
throw new IllegalArgumentException("No part must be null!");
556+
}
557+
String separator = "|";
558+
MessageDigest digest = MessageDigest.getInstance("SHA-256");
559+
final StringBuilder buffer = new StringBuilder(separator);
560+
parts.forEach(p -> {
561+
buffer.append(p).append(separator);
562+
});
563+
hash = digest.digest(buffer.toString().getBytes(StandardCharsets.UTF_8));
564+
}
565+
return hash;
566+
}
535567
}

src/test/java/eu/righettod/TestSecurityUtils.java

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -220,5 +220,34 @@ public void isPublicIPAddress() throws Exception {
220220
assertTrue(SecurityUtils.isPublicIPAddress(ip), String.format(templateMsgIPFalsePositive, ip));
221221
});
222222
}
223+
224+
@Test
225+
public void computeHashNoProneToAbuseOnParts() throws Exception {
226+
final String msgError = "Hash are expected to be different!";
227+
final String exceptionMsg = "No part must be null!";
228+
//Test cases for valid input passed
229+
List<String> reference = Arrays.asList("Hello from", " my amazing country", " in europe!");
230+
List<String> abuse1 = Arrays.asList("Hello fro", "m my amazing count", "ry in europe!");
231+
List<String> abuse2 = Arrays.asList("", "Hello from my amazing country in europe!", "");
232+
//--Ensure that source string are the sames when parts are joined
233+
assertEquals(String.join("", reference), String.join("", abuse1));
234+
assertEquals(String.join("", reference), String.join("", abuse2));
235+
assertEquals(String.join("", abuse1), String.join("", abuse2));
236+
//--Compute and validate hashes
237+
byte[] hashReference = SecurityUtils.computeHashNoProneToAbuseOnParts(reference);
238+
byte[] hashAbuse1 = SecurityUtils.computeHashNoProneToAbuseOnParts(abuse1);
239+
byte[] hashAbuse2 = SecurityUtils.computeHashNoProneToAbuseOnParts(abuse2);
240+
assertFalse(Arrays.equals(hashReference, hashAbuse1), msgError);
241+
assertFalse(Arrays.equals(hashReference, hashAbuse2), msgError);
242+
assertFalse(Arrays.equals(hashAbuse1, hashAbuse2), msgError);
243+
//Test case for invalid input passed
244+
List<String> invalidInput = Arrays.asList("Hello from", " my amazing country", " in europe!", null);
245+
IllegalArgumentException thrown = assertThrows(
246+
IllegalArgumentException.class,
247+
() -> SecurityUtils.computeHashNoProneToAbuseOnParts(invalidInput),
248+
"Expected IllegalArgumentException() to throw but invalid input was accepted!"
249+
);
250+
assertTrue(thrown.getMessage().contains(exceptionMsg));
251+
}
223252
}
224253

0 commit comments

Comments
 (0)