Friday, 24 November 2017

Perform password verification and validate (PBKDF2_ALGORITHM )?

package com.test.yuvagen.basic;

import java.security.SecureRandom;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.SecretKeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.spec.InvalidKeySpecException;
import javax.xml.bind.DatatypeConverter;

public class PasswordEncryption {

          @SuppressWarnings("serial")
          static public class InvalidHashException extends Exception {
                   public InvalidHashException(String message) {
                             super(message);
                   }

                   public InvalidHashException(String message, Throwable source) {
                             super(message, source);
                   }
          }

          @SuppressWarnings("serial")
          static public class CannotPerformOperationException extends Exception {
                   public CannotPerformOperationException(String message) {
                             super(message);
                   }

                   public CannotPerformOperationException(String message, Throwable source) {
                             super(message, source);
                   }
          }

          public static final String PBKDF2_ALGORITHM = "PBKDF2WithHmacSHA1";

          // These constants may be changed without breaking existing hashes.
          public static final int SALT_BYTE_SIZE = 24;
          public static final int HASH_BYTE_SIZE = 18;
          public static final int PBKDF2_ITERATIONS = 64000;

          // These constants define the encoding and may not be changed.
          public static final int HASH_SECTIONS = 5;
          public static final int HASH_ALGORITHM_INDEX = 0;
          public static final int ITERATION_INDEX = 1;
          public static final int HASH_SIZE_INDEX = 2;
          public static final int SALT_INDEX = 3;
          public static final int PBKDF2_INDEX = 4;

          public static String createHash(String password) throws CannotPerformOperationException {
                   return createHash(password.toCharArray());
          }

          public static String createHash(char[] password) throws CannotPerformOperationException {
                   // Generate a random salt
                   SecureRandom random = new SecureRandom();
                   byte[] salt = new byte[SALT_BYTE_SIZE];
                   random.nextBytes(salt);

                   // Hash the password
                   byte[] hash = pbkdf2(password, salt, PBKDF2_ITERATIONS, HASH_BYTE_SIZE);
                   int hashSize = hash.length;

                   // format: algorithm:iterations:hashSize:salt:hash
                   String parts = "sha1:" + PBKDF2_ITERATIONS + ":" + hashSize + ":" + toBase64(salt) + ":" + toBase64(hash);
                   return parts;
          }

          public static boolean verifyPassword(String password, String correctHash)
                             throws CannotPerformOperationException, InvalidHashException {
                   return verifyPassword(password.toCharArray(), correctHash);
          }

          public static boolean verifyPassword(char[] password, String correctHash)
                             throws CannotPerformOperationException, InvalidHashException {
                   // Decode the hash into its parameters
                   String[] params = correctHash.split(":");
                   if (params.length != HASH_SECTIONS) {
                             throw new InvalidHashException("Fields are missing from the password hash.");
                   }

                   // Currently, Java only supports SHA1.
                   if (!params[HASH_ALGORITHM_INDEX].equals("sha1")) {
                             throw new CannotPerformOperationException("Unsupported hash type.");
                   }

                   int iterations = 0;
                   try {
                             iterations = Integer.parseInt(params[ITERATION_INDEX]);
                   } catch (NumberFormatException ex) {
                             throw new InvalidHashException("Could not parse the iteration count as an integer.", ex);
                   }

                   if (iterations < 1) {
                             throw new InvalidHashException("Invalid number of iterations. Must be >= 1.");
                   }

                   byte[] salt = null;
                   try {
                             salt = fromBase64(params[SALT_INDEX]);
                   } catch (IllegalArgumentException ex) {
                             throw new InvalidHashException("Base64 decoding of salt failed.", ex);
                   }

                   byte[] hash = null;
                   try {
                             hash = fromBase64(params[PBKDF2_INDEX]);
                   } catch (IllegalArgumentException ex) {
                             throw new InvalidHashException("Base64 decoding of pbkdf2 output failed.", ex);
                   }

                   int storedHashSize = 0;
                   try {
                             storedHashSize = Integer.parseInt(params[HASH_SIZE_INDEX]);
                   } catch (NumberFormatException ex) {
                             throw new InvalidHashException("Could not parse the hash size as an integer.", ex);
                   }

                   if (storedHashSize != hash.length) {
                             throw new InvalidHashException("Hash length doesn't match stored hash length.");
                   }

                   // Compute the hash of the provided password, using the same salt,
                   // iteration count, and hash length
                   byte[] testHash = pbkdf2(password, salt, iterations, hash.length);
                   // Compare the hash in constant time. The password is correct if
                   // both hashes match.
                   return slowEquals(hash, testHash);
          }

          private static boolean slowEquals(byte[] a, byte[] b) {
                   int diff = a.length ^ b.length;
                   for (int i = 0; i < a.length && i < b.length; i++)
                             diff |= a[i] ^ b[i];
                   return diff == 0;
          }

          private static byte[] pbkdf2(char[] password, byte[] salt, int iterations, int bytes)
                             throws CannotPerformOperationException {
                   try {
                             PBEKeySpec spec = new PBEKeySpec(password, salt, iterations, bytes * 8);
                             SecretKeyFactory skf = SecretKeyFactory.getInstance(PBKDF2_ALGORITHM);
                             return skf.generateSecret(spec).getEncoded();
                   } catch (NoSuchAlgorithmException ex) {
                             throw new CannotPerformOperationException("Hash algorithm not supported.", ex);
                   } catch (InvalidKeySpecException ex) {
                             throw new CannotPerformOperationException("Invalid key spec.", ex);
                   }
          }

          private static byte[] fromBase64(String hex) throws IllegalArgumentException {
                   return DatatypeConverter.parseBase64Binary(hex);
          }

          private static String toBase64(byte[] array) {
                   return DatatypeConverter.printBase64Binary(array);
          }

}


package com.test.yuvagen.basic;

public class Test {

          public static void main(String[] args) {
                   basicTests();
                   truncatedHashTest();
                   testHashFunctionChecking();
          }

          // Make sure truncated hashes don't validate.
          public static void truncatedHashTest() {
                   String userString = "password!";
                   String goodHash = "";
                   String badHash = "";
                   int badHashLength = 0;

                   try {
                             goodHash = PasswordEncryption.createHash(userString);
                   } catch (Exception e) {
                             System.out.println(e.getMessage());
                             System.exit(1);
                   }
                   badHashLength = goodHash.length();

                   do {
                             badHashLength -= 1;
                             badHash = goodHash.substring(0, badHashLength);

                             boolean raised = false;
                             try {
                                      PasswordEncryption.verifyPassword(userString, badHash);
                             } catch (PasswordEncryption.InvalidHashException ex) {
                                      raised = true;
                             } catch (Exception ex) {
                                      System.out.println(ex.getMessage());
                                      System.exit(1);
                             }

                             if (!raised) {
                                      System.out.println("Truncated hash test: FAIL " + "(At hash length of " + badHashLength + ")");
                                      System.exit(1);
                             }

                             // The loop goes on until it is two characters away from the last :
                             // it
                             // finds. This is because the PBKDF2 function requires a hash that's
                             // at
                             // least 2 characters long.
                   } while (badHash.charAt(badHashLength - 3) != ':');

                   System.out.println("Truncated hash test: pass");
          }

          /**
           * Tests the basic functionality of the PasswordStorage class
           *
           * @param args
           *            ignored
           */
          public static void basicTests() {
                   try {
                             // Test password validation
                             boolean failure = false;
                             for (int i = 0; i < 10; i++) {
                                      String password = "New" + i;
                                      String hash = PasswordEncryption.createHash(password);
                                      String secondHash = PasswordEncryption.createHash(password);
                                      if (hash.equals(secondHash)) {
                                                System.out.println("FAILURE: TWO HASHES ARE EQUAL!");
                                                failure = true;
                                      }
                                      String wrongPassword = "New" + (i + 1);
                                      if (PasswordEncryption.verifyPassword(wrongPassword, hash)) {
                                                System.out.println("FAILURE: WRONG PASSWORD ACCEPTED!");
                                                failure = true;
                                      }
                                      if (!PasswordEncryption.verifyPassword(password, hash)) {
                                                System.out.println("FAILURE: GOOD PASSWORD NOT ACCEPTED!");
                                                failure = true;
                                      }
                             }
                             if (failure) {
                                      System.out.println("TESTS FAILED!");
                                      System.exit(1);
                             }
                   } catch (Exception ex) {
                             System.out.println("ERROR: " + ex);
                             System.exit(1);
                   }
          }

          public static void testHashFunctionChecking() {
                   try {
                             String hash = PasswordEncryption.createHash("foobar");
                             hash = hash.replaceFirst("sha1:", "sha256:");

                             boolean raised = false;
                             try {
                                      PasswordEncryption.verifyPassword("foobar", hash);
                             } catch (PasswordEncryption.CannotPerformOperationException ex) {
                                      raised = true;
                             }

                             if (raised) {
                                      System.out.println("Algorithm swap: pass");
                             } else {
                                      System.out.println("Algorithm swap: FAIL");
                                      System.exit(1);
                             }
                   } catch (Exception e) {
                             System.err.println(e.getMessage());
                             System.exit(1);
                   }

          }

}


Output:
Truncated hash test: pass

Algorithm swap: pass

No comments:

Post a Comment

Difference between final, finally and finalize()?

final :Final is a keyword. Final is used to apply restrictions on class, method and variable. Final class can't be inherited, final ...