I. Preface


The password Hash value is a sequence of encrypted characters calculated using a certain algorithm for the password provided by the user. There are a number of Hash algorithm implementations in Java that have been proven to be effective for password security, and I’ll discuss some of them in this article.


Note that once the Hash value of the password has been generated and stored in the database, you will not be able to convert it back to plain text. Only each time a user logs in to the application program, the Hash value must be generated to match the Hash value in the database to complete password verification.


. Two, simple password security implementation using MD5 algorithm


The MD5 message-digest Algorithm is a widely used encryption Hash function. It is mainly used to generate a 128bit(16byte) Hash value. Its implementation idea is very simple and easy to understand, its most basic idea is to map variable length data set to fixed length data set. To do this, it splits the input message into 512-bit chunks. Make sure its length can be divided by 512 by padding the message to the end. These blocks will now be processed by the MD5 algorithm and the result will be a 128-bit hash value. With MD5, the generated hash value is usually a 32-bit hexadecimal number.


In this document, the plain text of an encrypted password is called message, and the Hash value generated after the encryption is called Message Digest or digest. Here is an example of code for MD5 to generate a Hash value:



public class SimpleMD5Example

{

public static void main(String[] args)

{

String passwordToHash = “password”;

String generatedPassword = null;

try {

// Create MessageDigest instance for MD5

The 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);

}

}




Although MD5 is a popular Hash algorithm, it is not secure and the Hash values generated are quite weak. Its main advantages are fast generation and easy implementation. However, this also means that it is vulnerable to violent attacks and dictionary attacks. For example, a rainbow table generated using plaintext and Hash can quickly search for the raw data corresponding to a known Hash.


In addition, MD5 does not avoid Hash collisions: this means that different passwords will result in the same Hash value being generated.


However, if you still need to use MD5, consider adding salt to it to make it more secure. Using salt makes generated MD5 more secure


It is important to note that adding salt is not unique to MD5, and you can apply it to other algorithms as well. So, here you just need to focus on how it is applied rather than how it relates to MD5.


A salt is defined on Wikipedia as a one-way function that takes random data to add some extra data to a password or password. More simply, generate a Hash by generating some random text to append to the password.


The main purpose of adding salt to Hash is to prevent precomputed rainbow table attacks. The advantage of adding slat now is that it slows down the guessing of the password Hash value by changing the original comparison to multiple comparisons. Otherwise, the efficiency of cracking the Hash password library would be very high.


Important: In Java, we always need to use SecureRandom to generate a good salt value, so we can use the “SHA1PRNG” algorithm provided by the SecureRandom class to generate pseudorandom numbers. The code looks like this:



private static String getSalt() throws NoSuchAlgorithmException

{

//Always use a SecureRandom generator

SecureRandom sr = SecureRandom. GetInstance (" SHA1PRNG ");

//Create array for salt

byte[] salt = new byte[16];

//Get a random salt

sr.nextBytes(salt);

//return salt

return salt.toString();

}




SHA1PRNG algorithm is a pseudorandom number generator with high security based on SHA-1 algorithm. Note that if it is not supplied with a random number seed, it will generate a seed (TRNG) from a truly random number. TRGN and PRGN.


Next, take a look at an example of code for MD5 Hash plus slat:



public class SaltedMD5Example

{

public static void main(String[] args) throws NoSuchAlgorithmException, NoSuchProviderException

{

String passwordToHash = “password”;

String salt = getSalt();

String securePassword = getSecurePassword(passwordToHash, salt);

System.out.println(securePassword); //Prints 83ee5baeea20b6c21635e4ea67847f66

String regeneratedPassowrdToVerify = getSecurePassword(passwordToHash, salt);

System.out.println(regeneratedPassowrdToVerify); //Prints 83ee5baeea20b6c21635e4ea67847f66

}

private static String getSecurePassword(String passwordToHash, String salt)

{

String generatedPassword = null;

try {

// Create MessageDigest instance for MD5

The MessageDigest md = MessageDigest. GetInstance (" MD5 ");

//Add password bytes to digest

md.update(salt.getBytes());

/ / 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 String 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.toString();

}

}




Important note: Please note that you must now store its slat value for each password Hash. Because when the user logs into the system, you have to use the slAT originally generated to generate the Hash again to match the stored Hash. If you use different slATS (generating random SLAts) then the generated Hash will be different.


Plus, you’ve probably heard of Hash and salt multiple times. This usually means creating custom combinations, such as:


salt+password+salt => hash


You don’t really need to do this, because it doesn’t help you further enforce Hash security. If you need more security, the right thing to do is to choose a better algorithm. Intermediate password security implementations use the SHA algorithm


SHA (Secure Hash Algorithm) is also a member of the cryptographic Hash function family. It is very similar to MD5 except that it generates a more secure Hash than MD5. However, the Hash they generate is not always unique, which means that entering two different values yields the same Hash. Usually when this happens we call it a collision. However, SHA is less likely to collide than MD5. You don’t even have to worry about a collision, because it’s so rare.


There are four implementations of SHA in Java, which provides the following hash lengths compared to MD5(128 bit hash) :




  • Sha-1 (simple implementation – 160-bit 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 bit Hash)


The core idea is that longer hashes are usually harder to crack.


To obtain the corresponding algorithm implementation, the instance can be obtained by sending parameters to MessageDigest. As follows:



The MessageDigest md = MessageDigest. GetInstance (" SHA - 1 ");

//OR

The MessageDigest md = MessageDigest. GetInstance (" SHA - 256 ");




Here’s a look at SHA in action:



package com.howtodoinjava.hashing.password.demo.sha;

import java.security.MessageDigest;

import java.security.NoSuchAlgorithmException;

import java.security.SecureRandom;

public class SHAExample {

public static void main(String[] args) throws NoSuchAlgorithmException {

String passwordToHash = “password”;

String salt = getSalt();

String securePassword = get_SHA_1_SecurePassword(passwordToHash, salt);

System.out.println(securePassword);

securePassword = get_SHA_256_SecurePassword(passwordToHash, salt);

System.out.println(securePassword);

securePassword = get_SHA_384_SecurePassword(passwordToHash, salt);

System.out.println(securePassword);

securePassword = get_SHA_512_SecurePassword(passwordToHash, salt);

System.out.println(securePassword);

}

private static String get_SHA_1_SecurePassword(String passwordToHash, String salt)

{

String generatedPassword = null;

try {

The MessageDigest md = MessageDigest. GetInstance (" SHA - 1 ");

md.update(salt.getBytes());

byte[] bytes = md.digest(passwordToHash.getBytes());

StringBuilder sb = new StringBuilder();

for(int i=0; i< bytes.length ; i++)

{

sb.append(Integer.toString((bytes[i] & 0xff) + 0x100, 16).substring(1));

}

generatedPassword = sb.toString();

}

catch (NoSuchAlgorithmException e)

{

e.printStackTrace();

}

return generatedPassword;

}

private static String get_SHA_256_SecurePassword(String passwordToHash, String salt)

{

/ / Use the MessageDigest md = MessageDigest. GetInstance (" SHA - 256 ");

}

private static String get_SHA_384_SecurePassword(String passwordToHash, String salt)

{

/ / Use the MessageDigest md = MessageDigest. GetInstance (" SHA - 384 ");

}

private static String get_SHA_512_SecurePassword(String passwordToHash, String salt)

{

/ / Use the MessageDigest md = MessageDigest. GetInstance (" SHA - 512 ");

}

//Add salt

private static String getSalt() throws NoSuchAlgorithmException

{

SecureRandom sr = SecureRandom. GetInstance (" SHA1PRNG ");

byte[] salt = new byte[16];

sr.nextBytes(salt);

return salt.toString();

}

}




As shown in the code above, you can use SHA with salt to enhance its security. Higher password security implementation uses PBKDF2WithHmacSHA1 algorithm


So far, we’ve seen how to generate a secure Hash value for a password and how to enforce its security by leveraging salt. The problem today is that hardware is far faster than any violent attack using dictionaries or rainbow tables, and any password can be cracked, just for how long.


To solve this problem, the main idea is to minimize the speed of violent attacks. Our next algorithm is also based on this concept. The goal is to make the Hash function slow enough to prevent attacks, but still very fast for the user and not feel a significant delay.


This is usually done using some CPU intensive algorithm, such as PBKDF2, Bcrypt, or Scrypt. These algorithms take the work factor(also known as the security factor) or number of iterations as parameters to determine how slow the Hash function will be, and can gradually increase the work factor to balance it out as computing power increases in the future.


Java can be “PBKDF2WithHmacSHA1” to achieve the “PBKDF2” algorithm, the following code is an example:



public static void main(String[] args) throws NoSuchAlgorithmException, InvalidKeySpecException

{

String originalPassword = “password”;

String generatedSecuredPasswordHash = generateStorngPasswordHash(originalPassword);

System.out.println(generatedSecuredPasswordHash);

}

private static String generateStorngPasswordHash(String password) throws NoSuchAlgorithmException, InvalidKeySpecException

{

int iterations = 1000;

char[] chars = password.toCharArray();

byte[] salt = getSalt().getBytes();

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 String getSalt() throws NoSuchAlgorithmException

{

SecureRandom sr = SecureRandom. GetInstance (" SHA1PRNG ");

byte[] salt = new byte[16];

sr.nextBytes(salt);

return salt.toString();

}

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;

}

}




The following is a method implementation that needs to provide login password authentication when you come back:



public static void main(String[] args) throws NoSuchAlgorithmException, InvalidKeySpecException

{

String originalPassword = “password”;

String generatedSecuredPasswordHash = generateStorngPasswordHash(originalPassword);

System.out.println(generatedSecuredPasswordHash);

Boolean matched = validatePassword (" password ", generatedSecuredPasswordHash);

System.out.println(matched);

Matched = validatePassword (" password1 ", generatedSecuredPasswordHash);

System.out.println(matched);

}

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;

}




Be careful when referring to the above code, and if you find any problems please download the attached code at the end of this article.


More secure password implementation using Bcrypt and Scrypt algorithms


The idea behind bcrypt is similar to PBKDF2. There is no built-in implementation of the bcrypt algorithm to slow attackers, but you can still find and download its source code.


Here is an example of bcrypt in use (where bcrypt.java is provided in the source code) :



public class BcryptHashingExample

{

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);

}

}




Provide the bcrypt.java file download address: download


Like the Bcrypt algorithm, I’ve downloaded the source code for the Scrypt algorithm from Github and added it to the source code download in the last section. See how it works:



public class ScryptPasswordHashingDemo

{

public static void main(String[] args) {

String originalPassword = “password”;

String generatedSecuredPasswordHash = SCryptUtil.scrypt(originalPassword, 16, 16, 16);

System.out.println(generatedSecuredPasswordHash);

Boolean matched = SCryptUtil. Check (" password ", generatedSecuredPasswordHash);

System.out.println(matched);

Matched = SCryptUtil. Check (" passwordno ", generatedSecuredPasswordHash);

System.out.println(matched);

}

}




Iv. Final remarks


Storing passwords in plain text in an application is extremely dangerous. MD5 provides the most basic secure Hash generation. Slat should be added to it to further enhance its security. MD5 Generates a 128-bit Hash. To make it more secure, the SHA algorithm should be used to generate either 160-bit or 512-bit long hashes, with 512-bit being the strongest. Although using SHA Hash passwords can also be cracked by today’s fast hardware, to avoid this, you need algorithms that make violent attacks as slow and as impactful as possible. You can use PBKDF2, BCrypt or SCrypt algorithms. Select the appropriate security algorithm after careful consideration.




From:
www.cnblogs.com/interdrp/p/…


If there are any improper articles, please correct them. You can also follow my wechat public number:
Good good study Java, access to quality resources.