Skip to content

Commit

Permalink
Update security and poms
Browse files Browse the repository at this point in the history
  • Loading branch information
T5750 committed Feb 29, 2020
1 parent 3f9d2dd commit 982acfa
Show file tree
Hide file tree
Showing 14 changed files with 354 additions and 17 deletions.
3 changes: 2 additions & 1 deletion doc/source/security/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,5 @@ Security
dsa
rsa
https
SSL
SSL
secureHash
55 changes: 55 additions & 0 deletions doc/source/security/secureHash.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# Secure Hash

>Please remember that once this password hash is generated and stored in the database, you can not convert it back to the original password.
## Simple password security using MD5 algorithm
The [MD5 Message-Digest Algorithm](https://en.wikipedia.org/wiki/MD5) is a widely used [cryptographic hash function](https://en.wikipedia.org/wiki/Cryptographic_hash_function) that produces a 128-bit (16-byte) hash value. It’s very simple and straight forward; the **basic idea is to map data sets of variable length to data sets of a fixed length**.

- `MD5SaltMD5Simple`

It’s main advantages are that it is fast, and easy to implement. But it also means that it is susceptible to [brute-force](https://en.wikipedia.org/wiki/Brute-force_attack) and [dictionary attacks](https://en.wikipedia.org/wiki/Dictionary_attack).

## Making MD5 more secure using salt
>Wikipedia defines salt as **random data that are used as an additional input to a one-way function that hashes a password or pass-phrase**. In more simple words, salt is some randomly generated text, which is appended to the password before obtaining hash.
**Important**: We always need to use a `SecureRandom` to create good salts, and in Java, the `SecureRandom` class supports the `SHA1PRNG` pseudo random number generator algorithm, and we can take advantage of it.

`SHA1PRNG` algorithm is used as cryptographically strong pseudo-random number generator based on the `SHA-1` message digest algorithm. Note that if a seed is not provided, it will generate a **seed** from a true random number generator (**TRNG**).

- `MD5Salt`

**Important**: Please note that now you have to **store this salt value for every password you hash**. Because when user login back in system, you must use only originally generated salt to again create the hash to match with stored hash. If a different salt is used (we are generating random salt), then generated hash will be different.

## Medium password security using SHA algorithms
The [SHA (Secure Hash Algorithm)](https://en.wikipedia.org/wiki/Secure_Hash_Algorithm) is a family of cryptographic hash functions. It is very similar to MD5 except it **generates more strong hashes**.

Java has 4 implementations of SHA algorithm. They generate the following length hashes in comparison to MD5 (128-bit hash):
- `SHA-1` (Simplest one – 160 bits Hash)
- `SHA-256` (Stronger than `SHA-1` – 256 bits Hash)
- `SHA-384` (Stronger than `SHA-256` – 384 bits Hash)
- `SHA-512` (Stronger than `SHA-384` – 512 bits Hash)

`SHATest`

## Advanced password security using PBKDF2WithHmacSHA1 algorithm
This feature is essentially implemented using some **CPU intensive algorithms** such as **PBKDF2**, [Bcrypt](https://en.wikipedia.org/wiki/Bcrypt) or [Scrypt](https://en.wikipedia.org/wiki/Scrypt). These algorithms take a work factor (also known as security factor) or iteration count as an argument. This value determines how slow the hash function will be. When computers become faster next year we can increase the work factor to balance it out.

Java has implementation of “[PBKDF2](https://en.wikipedia.org/wiki/PBKDF2)” algorithm as “[PBKDF2WithHmacSHA1](https://docs.oracle.com/javase/7/docs/technotes/guides/security/StandardNames.html#SecretKeyFactory)“.
- `PBKDF2WithHmacSHA1Test`

## More Secure password hash using bcrypt and scrypt algorithms
### Java bcrypt with salt
- `BcryptTest`

### Java scrypt with salt
- `ScryptTest`

## Final Notes
1. Storing the text password with hashing is most dangerous thing for application security today.
2. MD5 provides basic hashing for generating secure password hash. Adding salt make it further stronger.
3. MD5 generates 128 bit hash. To make ti more secure, use SHA algorithm which generate hashes from 160-bit to 512-bit long. 512-bit is strongest.
4. Even SHA hashed secure passwords are able to be cracked with today’s fast hardwares. To beat that, you will need algorithms which can make the brute force attacks slower and minimize the impact. Such algorithms are PBKDF2, BCrypt and SCrypt.
5. Please take a well considered thought before applying appropriate security algorithm.

## References
- [Java Secure Hashing – MD5, SHA256, SHA512, PBKDF2, BCrypt, SCrypt](https://howtodoinjava.com/security/how-to-generate-secure-password-hash-md5-sha-pbkdf2-bcrypt-examples/)
5 changes: 0 additions & 5 deletions jdk7/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,10 @@

<artifactId>jdk7</artifactId>

<properties>
<log4j.version>1.2.17</log4j.version>
</properties>

<dependencies>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>${log4j.version}</version>
</dependency>
</dependencies>
</project>
7 changes: 0 additions & 7 deletions patterns/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -12,22 +12,15 @@
<groupId>com.evangel</groupId>
<artifactId>patterns</artifactId>

<properties>
<log4j.version>1.2.17</log4j.version>
<junit.version>4.11</junit.version>
</properties>

<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit.version}</version>
<!--<scope>test</scope>-->
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>${log4j.version}</version>
</dependency>
</dependencies>
</project>
23 changes: 23 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<java.version>1.7</java.version>
<log4j.version>1.2.17</log4j.version>
<junit.version>4.11</junit.version>
</properties>

<modules>
Expand All @@ -26,6 +28,22 @@
<module>jvm</module>
</modules>

<dependencyManagement>
<dependencies>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>${log4j.version}</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit.version}</version>
<!--<scope>test</scope>-->
</dependency>
</dependencies>
</dependencyManagement>

<build>
<finalName>${project.artifactId}</finalName>
<pluginManagement>
Expand All @@ -42,5 +60,10 @@
</plugin>
</plugins>
</pluginManagement>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
8 changes: 7 additions & 1 deletion security/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,10 @@
- [ECDSA](../doc/source/security/dsa.md#ecdsa)
- [RSA](../doc/source/security/rsa.md)
- [HTTPS](../doc/source/security/https.md)
- [SSL/TLS](../doc/source/security/SSL.md)
- [SSL/TLS](../doc/source/security/SSL.md)
- [Secure Hash](../doc/source/security/secureHash.md)

## Runtime Environment
- [Java 7](http://www.oracle.com/technetwork/java/javase/downloads/jdk7-downloads-1880260.html)
- [jBCrypt 0.4](http://www.mindrot.org/projects/jBCrypt/)
- [scrypt 1.4.0](https://github.com/wg/scrypt)
11 changes: 10 additions & 1 deletion security/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,19 @@
<artifactId>commons-codec</artifactId>
<version>1.10</version>
</dependency>
<dependency>
<groupId>org.mindrot</groupId>
<artifactId>jbcrypt</artifactId>
<version>0.4</version>
</dependency>
<dependency>
<groupId>com.lambdaworks</groupId>
<artifactId>scrypt</artifactId>
<version>1.4.0</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
</dependency>
</dependencies>
</project>
17 changes: 17 additions & 0 deletions security/src/main/java/t5750/security/securehash/BcryptTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package t5750.security.securehash;

import java.security.NoSuchAlgorithmException;

import org.mindrot.jbcrypt.BCrypt;

public class BcryptTest {
public static void main(String[] args) throws NoSuchAlgorithmException {
String originalPassword = "password";
String generatedSecuredPasswordHash = BCrypt.hashpw(originalPassword,
BCrypt.gensalt(12));
System.out.println(generatedSecuredPasswordHash);
boolean matched = BCrypt.checkpw(originalPassword,
generatedSecuredPasswordHash);
System.out.println(matched);
}
}
59 changes: 59 additions & 0 deletions security/src/main/java/t5750/security/securehash/MD5Salt.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package t5750.security.securehash;

import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.SecureRandom;

public class MD5Salt {
public static void main(String[] args)
throws NoSuchAlgorithmException, NoSuchProviderException {
String passwordToHash = "password";
byte[] salt = getSalt();
String securePassword = getSecurePassword(passwordToHash, salt);
System.out.println(securePassword);
String regeneratedPassowrdToVerify = getSecurePassword(passwordToHash,
salt);
System.out.println(regeneratedPassowrdToVerify);
}

private static String getSecurePassword(String passwordToHash,
byte[] salt) {
String generatedPassword = null;
try {
// Create MessageDigest instance for MD5
MessageDigest md = MessageDigest.getInstance("MD5");
// Add password bytes to digest
md.update(salt);
// Get the hash's bytes
byte[] bytes = md.digest(passwordToHash.getBytes());
// This bytes[] has bytes in decimal format;
// Convert it to hexadecimal format
StringBuilder sb = new StringBuilder();
for (int i = 0; i < bytes.length; i++) {
sb.append(Integer.toString((bytes[i] & 0xff) + 0x100, 16)
.substring(1));
}
// Get complete hashed password in hex format
generatedPassword = sb.toString();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
return generatedPassword;
}

/**
* Add salt
*/
private static byte[] getSalt()
throws NoSuchAlgorithmException, NoSuchProviderException {
// Always use a SecureRandom generator
SecureRandom sr = SecureRandom.getInstance("SHA1PRNG", "SUN");
// Create array for salt
byte[] salt = new byte[16];
// Get a random salt
sr.nextBytes(salt);
// return salt
return salt;
}
}
31 changes: 31 additions & 0 deletions security/src/main/java/t5750/security/securehash/MD5Simple.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package t5750.security.securehash;

import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

public class MD5Simple {
public static void main(String[] args) {
String passwordToHash = "password";
String generatedPassword = null;
try {
// Create MessageDigest instance for MD5
MessageDigest md = MessageDigest.getInstance("MD5");
// Add password bytes to digest
md.update(passwordToHash.getBytes());
// Get the hash's bytes
byte[] bytes = md.digest();
// This bytes[] has bytes in decimal format;
// Convert it to hexadecimal format
StringBuilder sb = new StringBuilder();
for (int i = 0; i < bytes.length; i++) {
sb.append(Integer.toString((bytes[i] & 0xff) + 0x100, 16)
.substring(1));
}
// Get complete hashed password in hex format
generatedPassword = sb.toString();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
System.out.println(generatedPassword);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
package t5750.security.securehash;

import java.math.BigInteger;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.spec.InvalidKeySpecException;

import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;

public class PBKDF2WithHmacSHA1Test {
public static void main(String[] args)
throws NoSuchAlgorithmException, InvalidKeySpecException {
String originalPassword = "password";
String generatedSecuredPasswordHash = generateStorngPasswordHash(
originalPassword);
System.out.println(generatedSecuredPasswordHash);
// validate the password again when user comes back and login
boolean matched = validatePassword("password",
generatedSecuredPasswordHash);
System.out.println(matched);
matched = validatePassword("password1", generatedSecuredPasswordHash);
System.out.println(matched);
}

private static String generateStorngPasswordHash(String password)
throws NoSuchAlgorithmException, InvalidKeySpecException {
int iterations = 1000;
char[] chars = password.toCharArray();
byte[] salt = getSalt();
PBEKeySpec spec = new PBEKeySpec(chars, salt, iterations, 64 * 8);
SecretKeyFactory skf = SecretKeyFactory
.getInstance("PBKDF2WithHmacSHA1");
byte[] hash = skf.generateSecret(spec).getEncoded();
return iterations + ":" + toHex(salt) + ":" + toHex(hash);
}

private static byte[] getSalt() throws NoSuchAlgorithmException {
SecureRandom sr = SecureRandom.getInstance("SHA1PRNG");
byte[] salt = new byte[16];
sr.nextBytes(salt);
return salt;
}

private static String toHex(byte[] array) throws NoSuchAlgorithmException {
BigInteger bi = new BigInteger(1, array);
String hex = bi.toString(16);
int paddingLength = (array.length * 2) - hex.length();
if (paddingLength > 0) {
return String.format("%0" + paddingLength + "d", 0) + hex;
} else {
return hex;
}
}

private static boolean validatePassword(String originalPassword,
String storedPassword)
throws NoSuchAlgorithmException, InvalidKeySpecException {
String[] parts = storedPassword.split(":");
int iterations = Integer.parseInt(parts[0]);
byte[] salt = fromHex(parts[1]);
byte[] hash = fromHex(parts[2]);
PBEKeySpec spec = new PBEKeySpec(originalPassword.toCharArray(), salt,
iterations, hash.length * 8);
SecretKeyFactory skf = SecretKeyFactory
.getInstance("PBKDF2WithHmacSHA1");
byte[] testHash = skf.generateSecret(spec).getEncoded();
int diff = hash.length ^ testHash.length;
for (int i = 0; i < hash.length && i < testHash.length; i++) {
diff |= hash[i] ^ testHash[i];
}
return diff == 0;
}

private static byte[] fromHex(String hex) throws NoSuchAlgorithmException {
byte[] bytes = new byte[hex.length() / 2];
for (int i = 0; i < bytes.length; i++) {
bytes[i] = (byte) Integer.parseInt(hex.substring(2 * i, 2 * i + 2),
16);
}
return bytes;
}
}

0 comments on commit 982acfa

Please sign in to comment.