Java – Create a Secure Password Hash

Learn Java hashing algorithms in-depth for hashing the passwords. A secure password hash is an encrypted sequence of characters obtained after applying specific algorithms and manipulations on user-provided passwords, which are generally very weak and easy to guess.

There are many such hashing algorithms in Java that can prove effective for password security.

Important

Please remember that once the password hash has been generated, we can not convert the hash back to the original password.

Each time a user login into the application, we must generate the password hash again and match it with the hash stored in the database.

So, if a user forgets his/her password, we will have to send him a temporary password; or ask him to reset the password. It’s common nowadays, right?

1. Simplest password hash with MD5 Algorithm

The MD5 Message-Digest Algorithm is a widely used cryptographic hash function that produces a 128-bit (16-byte) hash value. It’s very simple and straightforward; the basic idea is to map data sets of variable length to data sets of a fixed size.

To do this, the input message is split into chunks of 512-bit blocks. Padding is added to the end so that its length can be divided by 512.

These blocks are processed by the MD5 algorithm, which operates in a 128-bit state, and the result will be a 128-bit hash value. After applying MD5, the generated hash is typically a 32-digit hexadecimal number.

Here, the password to be encoded is often called the “message” and the generated hash value is called the message digest or simply “digest”.

1.1. Java MD5 Hashing Example

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

public class SimpleMD5Example 
{
  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);
  }
}
5f4dcc3b5aa765d61d8327deb882cf99 

1.2. Disadvatanges

  • Although MD5 is a widely spread hashing algorithm, is far from being secure, MD5 generates fairly weak hashes. Its main advantages are that it is fast, and easy to implement. But it also means that it is susceptible to brute-force and dictionary attacks.
  • Rainbow tables with words and hashes allow searching very quickly for a known hash and guessing the original password.
  • MD5 is not collision resistant which means that different passwords can eventually result in the same hash.

If you are using MD5 hash in your application, consider adding some salt to your security.

2. Making MD5 More Secure using Salt

Keep in mind, adding salt is not specific to MD5. We can add a Salt to every other algorithm also. So, please focus on how it is applied rather than its relation with MD5.

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.

The original intent of salting was primarily to defeat pre-computed rainbow table attacks that could otherwise be used to significantly improve the efficiency of cracking the hashed password database.

A more significant benefit is to slow down parallel operations that compare the hash of a password guess against many password hashes at once.

Important

We always need to use a SecureRandom to create good salts. The Java SecureRandom class supports the “SHA1PRNG” pseudo-random number generator algorithm, and we can take advantage of it.

2.1. How to generate Salt

Let’s see how we should generate 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();
}

SHA1PRNG algorithm is used as a 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).

2.2. Generate MD5 with Salt

Now, let’s look at the modified MD5 hashing example:

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

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

        String regeneratedPassowrdToVerify =
                getSecurePassword(passwordToHash, salt);

        System.out.println(regeneratedPassowrdToVerify);
    }

    private static String getSecurePassword(String passwordToHash,
            String salt) {
        String generatedPassword = null;
        try {
            // Create MessageDigest instance for MD5
            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();
    }
}

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

Also, you might hear of the terms crazy hashing and salting. It generally refers to creating custom combinations.

alt + password + salt => Generated hash

Do not practice these crazy things. They do not help in making hashes further secure anyhow. If you want more security, choose a better algorithm.

3. Better Password Security using SHA Algorithms

The SHA (Secure Hash Algorithm) is a family of cryptographic hash functions. It is very similar to MD5, except it generates more strong hashes.

However, SHA hashes are not always unique, and it means that we could have equal hashes for two different inputs. When this happens, it’s called a “collision”. The chances of collision in SHA are less than MD5. But, do not worry about these collisions because they are very rare.

Java has four implementations of the 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)

A longer hash is more challenging to break. That’s the core idea.

To get any implementation of the algorithm, pass it as a parameter to MessageDigest. e.g.

MessageDigest md = MessageDigest.getInstance("SHA-512");

//OR

MessageDigest md = MessageDigest.getInstance("SHA-256");

3.1. Java SHA Hashing Example

Let’s create a test program to demonstrate SHA hash generation:

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 {
            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) {
        String generatedPassword = null;
        try {
            MessageDigest md = MessageDigest.getInstance("SHA-256");
            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_384_SecurePassword(String passwordToHash,
            String salt) {
        String generatedPassword = null;
        try {
            MessageDigest md = MessageDigest.getInstance("SHA-384");
            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_512_SecurePassword(String passwordToHash,
            String salt) {
        String generatedPassword = null;
        try {
            MessageDigest md = MessageDigest.getInstance("SHA-512");
            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;
    }

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

87adfd14a7a89b201bf6d99105b417287db6581d8aee989076bb7f86154e8f32

bc5914fe3896ae8a2c43a4513f2a0d716974cc305733847e3d49e1ea52d1ca50e2a9d0ac192acd43facfb422bb5ace88

529211542985b8f7af61994670d03d25d55cc9cd1cff8d57bb799c4b586891e112b197530c76744bcd7ef135b58d47d65a0bec221eb5d77793956cf2709dd012

Very quickly, we can say that SHA-512 generates the most robust Hash.

4. More Strong Hashes using PBKDF2WithHmacSHA1 Algorithm

So far, we have learned about creating secure hashes for passwords and using salt to make it even more secure. But the problem today is that hardwares have become so fast than any brute force attack using dictionary and rainbow tables, a bad actor can crack any password in less or more time.

To solve this problem, the general idea is to make brute force attacks slower to minimize damage. Our following algorithm works on this very concept.

The goal is to make the hash function slow enough to impede attacks but still fast enough to not cause a noticeable delay for the user.

This feature is essentially implemented using some CPU-intensive algorithms such as PBKDF2, Bcrypt or Scrypt. These algorithms take a work factor (also known as security factor) or iteration count as an argument.

Iteration count 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 implemented “PBKDF2” algorithm as “PBKDF2WithHmacSHA1“.

4.1. Java PBKDF2WithHmacSHA1 Hash Example

Let’s look at the example of how to use PBKDF2WithHmacSHA1 algorithm.

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

    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;
    }
}
1000:5b4240333032306164:f38d165fce8ce42f59d366139ef5d9e1ca1247f0e06e503ee1a611dd9ec40876bb5edb8409f5abe5504aab6628e70cfb3d3a18e99d70357d295002c3d0a308a0

4.2. Verifying Passwords

The next step is to have a function that we can use to validate the password again when the user comes back and login.

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

Please refer to functions from the above code samples. If found any difficulty, then download the source code attached at the end of the tutorial.

5. Hashes using Bcrypt and Scrypt

The concepts behind bcrypt is similar to the previous concept as in PBKDF2. It just happened to be that Java does not have any inbuilt support for bcrypt algorithm to make the attack slower but still, you can find one such implementation in the attached source code.

5.1. Creating Hash using Bcrypt with Salt

Let’s look at the sample usage code (BCrypt.java is available 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);
	}
}
$2a$12$WXItscQ/FDbLKU4mO58jxu3Tx/mueaS8En3M6QOVZIZLaGdWrS.pK
true

5.2. Creating Hash using Scrypt with Salt

Like bcrypt, I have downloaded scrypt from github and added the source code of the scrypt algorithm in the sourcecode.

Let’s see how to use the implementation:

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);
	}
}
$s0$41010$Gxbn9LQ4I+fZ/kt0glnZgQ==$X+dRy9oLJz1JaNm1xscUl7EmUFHIILT1ktYB5DQ3fZs=
true
false

6. Conclusion

  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.

To download the source code of the above algorithm examples, please follow the below link.

Happy Learning !!

Download sourcecode

Was this post helpful?

Join 7000+ Awesome Developers

Get the latest updates from industry, awesome resources, blog updates and much more.

* We do not spam !!

92 thoughts on “Java – Create a Secure Password Hash”

  1. private String hashPassword(String password) {
    try {
    byte[] salt = new byte[32];
    SecretKeyFactory secretKeyFactory = SecretKeyFactory.getInstance(“PBKDF2WithHmacSHA1”);
    PBEKeySpec pbeKeySpec = new PBEKeySpec(password.toCharArray(), salt, 10000, 512);
    SecretKey secretKey = secretKeyFactory.generateSecret(pbeKeySpec);
    return new String(secretKey.getEncoded());
    } catch (NoSuchAlgorithmException exp) {
    throw new RuntimeException(exp);
    } catch (InvalidKeySpecException exp){
    throw new RuntimeException(exp);
    }

    }

    For the above code , JDK6 and JDK8 producing different results. Results are not matching, we are in migration from JDK6 to JDK8. Password already been used by multiple users, when we migrated to JDK8 users are not able to authenticate because of above code producing different results.

    Reply
  2. Right, so I’ve managed to hash my passwords and insert them into my database. But now how will I read from the database and allow users to login? Any help would be greatly appreciated!

    Kind Regards,
    Martin

    Reply
  3. hi i have a proble like Mohammed Nadeem but i don’t know how it works for him.
    please help. always validatePassword return false.

    try{
    	
                    con = DriverManager.getConnection(url,user,password);
    
                    String query = "select * from user where ID=?";
                     
                    pst = con.prepareStatement(query);
                    pst.setString(1, id.getText());
                    rs = pst.executeQuery();
    
                    while(rs.next()){
                   
    if(id.getText().equals(rs.getString("ID")) && validatePassword(pass.getText(),rs.getString("Pass"))){
    
    Login...
                    }
                    else{
    
    Login Failed!
    
                    }
    
    }catch(SQLException m){
                    m.printStackTrace();
                }
            }
    
    
    
    Reply
  4. Hey there, I got a quick question:
    I have removed the iterations from the output (1000:) and I use

     int iterations = 1000; 

    instead in the validatePassword method. Now I am wondering if it is safe to store the salt and the hashed password with the colon (:) or together in an sql database. If I do store them together, then I will always need to split them whenever validating, so that would mean I need to split after 16th byte, or after 32th character to get my salt. What do you suggest?

    Reply
  5. MD5 has been utilized in a wide variety of cryptographic applications, and is also commonly used to verify data integrity. It is widely used in java , Thanks for sharing.

    Reply
  6. Instead of using validatePassword() method, I want to decrypted method for getting the original password , incase of forgot password option,,can any one get this method for me please.

    Reply
    • It is not possible in above discussed techniques because You’re HASHING, not ENCRYPTING!

      The difference is that hashing is a one way function, where encryption is a two-way function.

      Reply
    • PBKDF2 can be also used to generate a salt to use in SHA functions, making the output of the PBKDF2 public.
      The salt need to be public, and secret key private.
      And HMAC is better to check integrity and authenticity, no to hash passwords. Am i right bro?

      Reply
  7. Hi,
    Excellent post. I want to use PBKDF2 when generating password hash. When i used “PBKDF2” algorithm as “PBKDF2WithHmacSHA1“ you wrote, i generate password with PBKDF2 but when i validated it for the same password i get false respond. Why i get false respond , i should take true respond. Do you now why i took false respond.
    Thank you!!
    Seda,

    Reply
  8. private static String getSalt() throws NoSuchAlgorithmException
    {
    SecureRandom sr = SecureRandom.getInstance(“SHA1PRNG”);
    byte[] salt = new byte[16];
    sr.nextBytes(salt);
    return salt.toString(); // maybe there is some error here
    }

    Reply
  9. Hi
    Thanks for your Post I successfully implements the BCrypt Hashing and store in database but when we fetched it from database and compare it will give an error “Password not Match” or not be able to login successfully.

    How to overcome with that problem?

    Reply
          •  
            String user_name=request.getParameter("userName");
            String originalPassword=request.getParameter("password");
            
            String generatedSecuredPasswordHash = BCrypt.hashpw(originalPassword, BCrypt.gensalt(12));
                System.out.println(generatedSecuredPasswordHash);
               
            
            try{
            
            		
            			con=DBUtility.getConnection();
            			stmt=con.prepareStatement("select * from login where userName=? and Password=?");
            			stmt.setString(1, user_name);
            			stmt.setString(2,generatedSecuredPasswordHash );
            			
            			rs=stmt.executeQuery();
            			
            			while(rs.next()){
            				
            				un=rs.getString("UserName");
            				pwd=rs.getString("Password");
            			
            				
            			}	
            			if(user_name.equals(un) && BCrypt.checkpw(originalPassword, pwd)){
            				session.setAttribute("check", user_name);
            				response.sendRedirect("Success.jsp");
            			}else{
            					System.out.print("Login Error");
            
            				}
            }catch(SQLException ex){
            				ex.printStackTrace();
            			}
            		}
          • Not sure what do mean by ‘horrible’ here.
            Can you please execute query only on username i.e. “select * from login where userName=?” and then compare generated password and password fetched from database. Also print them in logs to compare yourself. Also verify column size of password column.

          • Hi Lokesh

            Thanks its working Great..
            The Word Horrible we used is for my coding not for your content.
            Thanks Alot..

          • hi i have the same problem like Mohammed Nadeem but i don’t know how it works for him.
            please help always false even if it is true.

            try{
            	
            con = DriverManager.getConnection(url,user,password);
            
                            String query = "select * from user where ID=?";
                             
                            pst = con.prepareStatement(query);
                            pst.setString(1, id.getText());
                            rs = pst.executeQuery();
            
                            while(rs.next()){
                           
            if(id.getText().equals(rs.getString("ID")) && validatePassword(pass.getText(),rs.getString("Pass"))){
            
            Login...
                            }
                            else{
            
            Login Failed!
            
                            }
            
            }catch(SQLException m){
                            m.printStackTrace();
                        }
                    }
            
            
            
          • public static int check_user(String user_name, String password)
            {

            ArrayList arr = get_users();
            int x=1;

            for(int i=0; i< arr.size();i++)
            {
            if (arr.get(i).getUser_name().equals(user_name.toLowerCase()))
            {
            if(arr.get(i).getPassword().equals(password))
            {
            x=1;
            break; // True login
            }
            else
            {
            x=2; // False password
            break;
            }
            }
            else
            {
            x=0; // False username
            }
            }
            return x;
            }

  10. Your post is very helpful is but you have a SERIOUS BUG in your implementation, (which was already pointed some comments above)
    1) Your getSalt() method returns “salt.toString()”. This will return the hashcode of the byte-Array not the string representation, so it will always return something like “[B@1ce56f8”. In other words your current getSalt() implementation makes nothing else than “new Object().hashCode()”
    This is the main serious bug.
    2) Generally converting a random byte array to String is not the right idea. Some byte sequences can’t be converted to a valid utf8 text.
    This is a minor bug.

    My advice: use the salt as byte[]. If you don’t want to handle a byte array, convert it to a long (e.g. using ByteBuffer). If you really need a string , convert the salt to a hex representation of the byte sequence.

    Reply
  11. Also , what would be size of key length below line return, what I Understand the key length we are passing is 64*8 = 512, but the password length it it return after hashing is 128.
    PBEKeySpec spec = new PBEKeySpec(chars, salt, iterations, 64 * 8);

    Can you please explain how it is determining key length.

    Reply
  12. I’m curious about why validatePassword method, why not just hash the new pw with the same salt and see if the answer is the same? Is there a reason to do it the way you’re doing it (it just seems a little more complicated)?

    Reply
    • validatePassword() method does exactly he same thing. You can choose to do it differently .. may be compare using database query.. but the logic remains the same : hash the new password with the same salt and compare with stored password.

      Reply
  13. Hello Lokesh,

    Great article. My question if you store the string from generateStrongPasswordHash which contains the PBKDF2 work factor and salt in the string does this make the algorithm weak again in the sense the hacker would know both the salt and the work factor?

    Thanks,
    Lloyd

    Reply
    • Work factor is for delaying the computation speed.. even if hacker know it’s value.. he can’t manipulate the execution speed in runtime.
      Salt also will not help in guessing the password. How it can?

      Reply
  14. Why there is no date for the article? I can see it’s in the URL, and from the comments I could have guessed, but why is there no date to clearly show at what time this was written? Computer science is moving fast enough, that stuff that was written 2 years ago, might not be true today.

    Reply
  15. I want to encrypt zip file that i generated. inside the zip file I have files.
    So I want to encrypt the zip file. Its my first time to work with encryption. I did my own research, I seem to understand but not clearly on how it works.
    Can anyone help with a clear example, and good way of encrypting, please explain to me. And how do I implement it.

    Example of encrypting a zip file will be a good one.

    Your help will be appreciated.

    Thanks in advanced

    Reply
      • Lokesh Thanks, But I tried that one, it leave a file that is corrupt. I cant access it to see if its encrypted or not.

        Reply
        • I tried to open the file using double click, and it was format error as you said. But when I tried 7-zip software, it asked for password and accepted correct password also. Let me try few things.

          Reply
          • Lokesh, its work as a charm after I replace the parameters.setEncryptionMethod(Zip4jConstants.ENC_METHOD_AES);

            with
            parameters.setEncryptionMethod(Zip4jConstants.ENC_METHOD_STANDARD);

            Thanks a lot, but now in my case. I generate two files in my app on android. The format of the files are .txt and .mp4, i than zip them together. Which happens successfully. Now I want to encrypt the zip file to another extension (.ecz) and put a password to it. So I will be having the zip file, which is not encrypted and the encrypted file which is the one I need. Zip file and the two files, I will delete them. I will be left with encrypted file with this extension .ecz

            I will be happy if you help me accomplish this, i battle this for three days now.

  16. Thanks for a great tutorial.
    I wanna send encrypted to image to server(android).Now please can you help me out how can i achieve this???

    Reply
  17. Hi,

    In the function specified to get salt.

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

    You are generating the salt by using “SHA1PRNG” but while returning you are sending the hashcode of salt. So all the efforts made to generate the secure random salt is ruined.

    Instead construct new string from the salt and return.
    private static String getSalt() throws NoSuchAlgorithmException
    {
    SecureRandom sr = SecureRandom.getInstance(“SHA1PRNG”);
    byte[] salt = new byte[16];
    sr.nextBytes(salt);
    return new String(salt);
    }

    Reply
  18. Hi Lokesh,

    I have gone thru one of the website which discourage use of “SHA1-PRNG” Algo. for using Random Generator.
    Please have a look

    Reply
    • Himansu, the only reason I found on quick google search is “sha1prng” is slightly slower than native algorithms like ‘Windows-PRNG’ on windows and ‘NativePRNG’ in linux. No where I could find any bench-mark data about their performances.
      So my take is that you can go with sha1prng as long as you do not hit any performance roadblock, specifically due to random generator. In all normal real life scenarios, sha1prng is more than enough.

      Reply
  19. Great post! One question though. If I wanted to display the hashed password as a String to the user, how would I get that value if I am using JUST the hashed password from a database? I am using the advanced technique to store a password

    Reply
    • I have not mis-understood your question then you need to fetch the password from database, just like you fetch any other field. I really doubt what you mean by “hashed password as a String”? Isn’t hashed password a string itself?

      Reply
      • I am sorry I should be more clear. I have an administrative user and a student user. I would like the administrator to have access to all of the student’s original passwords. So when I query my database, how can I decode the hashed password back to the original, plain text password for the admin to view? After reading some other comments, I see that it is impossible to get the original password after using the advanced hash function that you provided. Can you suggest a way around this?

        Reply
    • Please re-read the third para in starting “Note: Please remember that once this password hash is generated and stored in database, you can not convert it back to original password. Each time user login into application, you have to regenerate password hash again, and match with hash stored in database.”

      Reply
      • Thank you! I understand it! But, hash each time is the different for the same strings. So, hashes of stored and input strings constantly would be different, even strings are the same. Maybe, it is because of using salt. So this code will not work correctly.
        String passwordToHash = "password";
        String salt = getSalt();
        String securePassword = get_SHA_1_SecurePassword(passwordToHash, salt);

        String passwordEntered = "password";
        String salt2 = getSalt();
        String securePasswordEntered = get_SHA_1_SecurePassword(passwordEntered, salt2);

        securePasswordEntered.equals(securePassword);

        Reply
        • Another similar note is written in “Making MD5 more secure using salt” section:
          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.

          Reply
      • It will make you use the hash code of byte array ‘object’ rather than the generated random byte values themselves.
        More precisely, it is getClass().getName() + ‘@’ + Integer.toHexString(hashCode()).

        Reply
        • I think your getSalt() should be revised like below.


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

          Reply
          • It looks SecureRandom.getInstance(“SHA1PRNG”) returns a new instance of SecureRandom every time you call it. Just wonder, if it is a good approach to create a new SecureRandom everytime we need a salt??

          • Not sure if there is any secured ThreadLocalSecuredRandom? Just puzzled about making it singleton or thread local…

  20. Hi, If I would be using SHA 256

    will i need to change something here in the getSalt()? like change the SHA1PRNG to SHA256PRNG? and the byte size? or Is it okay to use this on SHA 256 w/o changing anything

    //Add salt
    private static String getSalt() throws NoSuchAlgorithmException
    {
    SecureRandom sr = SecureRandom.getInstance(“SHA1PRNG”);
    byte[] salt = new byte[16];
    sr.nextBytes(salt);
    return salt.toString();
    }

    Reply
  21. A comment about your PBKDF2WithHmacSHA1 solution:

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

    You could just return salt without converting it to String in

    return salt.toString();

    and then converting it back to byte array here:

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

    By just using

    return salt;

    in getSalt(), and then:

    byte[] salt = getSalt();
    in generateStorngPasswordHash().

    Reply
  22. I don’t like the implementation of the getSalt function. You convert the byte array to string (which does not make any sense at all, as a random variable will not have any sensible string representation), to convert the value back to byte array in the next step. More to it, you create an object with cryptographic data in memory you will not be able to override (because of immutability). This might be acceptable in this example, but it is a very bad idea in crypto. No FIPS 140-2 for you. 😉

    Reply

Leave a Comment

HowToDoInJava

A blog about Java and related technologies, the best practices, algorithms, and interview questions.