Advertisement
BaSs_HaXoR

Java PBKDF2 Password Hashing Code

Sep 9th, 2014
423
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Java 8.90 KB | None | 0 0
  1. /*
  2.  * Password Hashing With PBKDF2 (http://crackstation.net/hashing-security.htm).
  3.  * Copyright (c) 2013, Taylor Hornby
  4.  * All rights reserved.
  5.  *
  6.  * Redistribution and use in source and binary forms, with or without
  7.  * modification, are permitted provided that the following conditions are met:
  8.  *
  9.  * 1. Redistributions of source code must retain the above copyright notice,
  10.  * this list of conditions and the following disclaimer.
  11.  *
  12.  * 2. Redistributions in binary form must reproduce the above copyright notice,
  13.  * this list of conditions and the following disclaimer in the documentation
  14.  * and/or other materials provided with the distribution.
  15.  *
  16.  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  17.  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  18.  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  19.  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
  20.  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
  21.  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
  22.  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
  23.  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
  24.  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  25.  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  26.  * POSSIBILITY OF SUCH DAMAGE.
  27.  */
  28.  
  29. import java.security.SecureRandom;
  30. import javax.crypto.spec.PBEKeySpec;
  31. import javax.crypto.SecretKeyFactory;
  32. import java.math.BigInteger;
  33. import java.security.NoSuchAlgorithmException;
  34. import java.security.spec.InvalidKeySpecException;
  35.  
  36. /*
  37.  * PBKDF2 salted password hashing.
  38.  * Author: havoc AT defuse.ca
  39.  * www: http://crackstation.net/hashing-security.htm
  40.  */
  41. public class PasswordHash
  42. {
  43.     public static final String PBKDF2_ALGORITHM = "PBKDF2WithHmacSHA1";
  44.  
  45.     // The following constants may be changed without breaking existing hashes.
  46.     public static final int SALT_BYTE_SIZE = 24;
  47.     public static final int HASH_BYTE_SIZE = 24;
  48.     public static final int PBKDF2_ITERATIONS = 1000;
  49.  
  50.     public static final int ITERATION_INDEX = 0;
  51.     public static final int SALT_INDEX = 1;
  52.     public static final int PBKDF2_INDEX = 2;
  53.  
  54.     /**
  55.      * Returns a salted PBKDF2 hash of the password.
  56.      *
  57.      * @param   password    the password to hash
  58.      * @return              a salted PBKDF2 hash of the password
  59.      */
  60.     public static String createHash(String password)
  61.         throws NoSuchAlgorithmException, InvalidKeySpecException
  62.     {
  63.         return createHash(password.toCharArray());
  64.     }
  65.  
  66.     /**
  67.      * Returns a salted PBKDF2 hash of the password.
  68.      *
  69.      * @param   password    the password to hash
  70.      * @return              a salted PBKDF2 hash of the password
  71.      */
  72.     public static String createHash(char[] password)
  73.         throws NoSuchAlgorithmException, InvalidKeySpecException
  74.     {
  75.         // Generate a random salt
  76.         SecureRandom random = new SecureRandom();
  77.         byte[] salt = new byte[SALT_BYTE_SIZE];
  78.         random.nextBytes(salt);
  79.  
  80.         // Hash the password
  81.         byte[] hash = pbkdf2(password, salt, PBKDF2_ITERATIONS, HASH_BYTE_SIZE);
  82.         // format iterations:salt:hash
  83.         return PBKDF2_ITERATIONS + ":" + toHex(salt) + ":" +  toHex(hash);
  84.     }
  85.  
  86.     /**
  87.      * Validates a password using a hash.
  88.      *
  89.      * @param   password        the password to check
  90.      * @param   correctHash     the hash of the valid password
  91.      * @return                  true if the password is correct, false if not
  92.      */
  93.     public static boolean validatePassword(String password, String correctHash)
  94.         throws NoSuchAlgorithmException, InvalidKeySpecException
  95.     {
  96.         return validatePassword(password.toCharArray(), correctHash);
  97.     }
  98.  
  99.     /**
  100.      * Validates a password using a hash.
  101.      *
  102.      * @param   password        the password to check
  103.      * @param   correctHash     the hash of the valid password
  104.      * @return                  true if the password is correct, false if not
  105.      */
  106.     public static boolean validatePassword(char[] password, String correctHash)
  107.         throws NoSuchAlgorithmException, InvalidKeySpecException
  108.     {
  109.         // Decode the hash into its parameters
  110.         String[] params = correctHash.split(":");
  111.         int iterations = Integer.parseInt(params[ITERATION_INDEX]);
  112.         byte[] salt = fromHex(params[SALT_INDEX]);
  113.         byte[] hash = fromHex(params[PBKDF2_INDEX]);
  114.         // Compute the hash of the provided password, using the same salt,
  115.         // iteration count, and hash length
  116.         byte[] testHash = pbkdf2(password, salt, iterations, hash.length);
  117.         // Compare the hashes in constant time. The password is correct if
  118.         // both hashes match.
  119.         return slowEquals(hash, testHash);
  120.     }
  121.  
  122.     /**
  123.      * Compares two byte arrays in length-constant time. This comparison method
  124.      * is used so that password hashes cannot be extracted from an on-line
  125.      * system using a timing attack and then attacked off-line.
  126.      *
  127.      * @param   a       the first byte array
  128.      * @param   b       the second byte array
  129.      * @return          true if both byte arrays are the same, false if not
  130.      */
  131.     private static boolean slowEquals(byte[] a, byte[] b)
  132.     {
  133.         int diff = a.length ^ b.length;
  134.         for(int i = 0; i < a.length && i < b.length; i++)
  135.             diff |= a[i] ^ b[i];
  136.         return diff == 0;
  137.     }
  138.  
  139.     /**
  140.      *  Computes the PBKDF2 hash of a password.
  141.      *
  142.      * @param   password    the password to hash.
  143.      * @param   salt        the salt
  144.      * @param   iterations  the iteration count (slowness factor)
  145.      * @param   bytes       the length of the hash to compute in bytes
  146.      * @return              the PBDKF2 hash of the password
  147.      */
  148.     private static byte[] pbkdf2(char[] password, byte[] salt, int iterations, int bytes)
  149.         throws NoSuchAlgorithmException, InvalidKeySpecException
  150.     {
  151.         PBEKeySpec spec = new PBEKeySpec(password, salt, iterations, bytes * 8);
  152.         SecretKeyFactory skf = SecretKeyFactory.getInstance(PBKDF2_ALGORITHM);
  153.         return skf.generateSecret(spec).getEncoded();
  154.     }
  155.  
  156.     /**
  157.      * Converts a string of hexadecimal characters into a byte array.
  158.      *
  159.      * @param   hex         the hex string
  160.      * @return              the hex string decoded into a byte array
  161.      */
  162.     private static byte[] fromHex(String hex)
  163.     {
  164.         byte[] binary = new byte[hex.length() / 2];
  165.         for(int i = 0; i < binary.length; i++)
  166.         {
  167.             binary[i] = (byte)Integer.parseInt(hex.substring(2*i, 2*i+2), 16);
  168.         }
  169.         return binary;
  170.     }
  171.  
  172.     /**
  173.      * Converts a byte array into a hexadecimal string.
  174.      *
  175.      * @param   array       the byte array to convert
  176.      * @return              a length*2 character string encoding the byte array
  177.      */
  178.     private static String toHex(byte[] array)
  179.     {
  180.         BigInteger bi = new BigInteger(1, array);
  181.         String hex = bi.toString(16);
  182.         int paddingLength = (array.length * 2) - hex.length();
  183.         if(paddingLength > 0)
  184.             return String.format("%0" + paddingLength + "d", 0) + hex;
  185.         else
  186.             return hex;
  187.     }
  188.  
  189.     /**
  190.      * Tests the basic functionality of the PasswordHash class
  191.      *
  192.      * @param   args        ignored
  193.      */
  194.     public static void main(String[] args)
  195.     {
  196.         try
  197.         {
  198.             // Print out 10 hashes
  199.             for(int i = 0; i < 10; i++)
  200.                 System.out.println(PasswordHash.createHash("p\r\nassw0Rd!"));
  201.  
  202.             // Test password validation
  203.             boolean failure = false;
  204.             System.out.println("Running tests...");
  205.             for(int i = 0; i < 100; i++)
  206.             {
  207.                 String password = ""+i;
  208.                 String hash = createHash(password);
  209.                 String secondHash = createHash(password);
  210.                 if(hash.equals(secondHash)) {
  211.                     System.out.println("FAILURE: TWO HASHES ARE EQUAL!");
  212.                     failure = true;
  213.                 }
  214.                 String wrongPassword = ""+(i+1);
  215.                 if(validatePassword(wrongPassword, hash)) {
  216.                     System.out.println("FAILURE: WRONG PASSWORD ACCEPTED!");
  217.                     failure = true;
  218.                 }
  219.                 if(!validatePassword(password, hash)) {
  220.                     System.out.println("FAILURE: GOOD PASSWORD NOT ACCEPTED!");
  221.                     failure = true;
  222.                 }
  223.             }
  224.             if(failure)
  225.                 System.out.println("TESTS FAILED!");
  226.             else
  227.                 System.out.println("TESTS PASSED!");
  228.         }
  229.         catch(Exception ex)
  230.         {
  231.             System.out.println("ERROR: " + ex);
  232.         }
  233.     }
  234.  
  235. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement