-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
14 changed files
with
354 additions
and
17 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -9,4 +9,5 @@ Security | |
dsa | ||
rsa | ||
https | ||
SSL | ||
SSL | ||
secureHash |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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/) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
17 changes: 17 additions & 0 deletions
17
security/src/main/java/t5750/security/securehash/BcryptTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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
59
security/src/main/java/t5750/security/securehash/MD5Salt.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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
31
security/src/main/java/t5750/security/securehash/MD5Simple.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | ||
} | ||
} |
83 changes: 83 additions & 0 deletions
83
security/src/main/java/t5750/security/securehash/PBKDF2WithHmacSHA1Test.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
} | ||
} |
Oops, something went wrong.