aboutsummaryrefslogtreecommitdiff
path: root/src/org/traccar/helper/PasswordHash.java
diff options
context:
space:
mode:
authorAnton Tananaev <anton.tananaev@gmail.com>2015-06-17 09:54:02 +1200
committerAnton Tananaev <anton.tananaev@gmail.com>2015-06-17 09:54:02 +1200
commit771e2d7c4ceb34c0b62852130061b04640b8ee71 (patch)
tree57a23077fc9af137baffbb51bcb4ba82cff2f94b /src/org/traccar/helper/PasswordHash.java
parent8ff799f9d16715259131cd535f7f918823f161f9 (diff)
parent92ac9aaa10fcf65a005c4e06245ce4a9427d5148 (diff)
downloadtrackermap-server-771e2d7c4ceb34c0b62852130061b04640b8ee71.tar.gz
trackermap-server-771e2d7c4ceb34c0b62852130061b04640b8ee71.tar.bz2
trackermap-server-771e2d7c4ceb34c0b62852130061b04640b8ee71.zip
Merge pull request #1252 from demianalonso/password-salt
Implemented password hashing with salt
Diffstat (limited to 'src/org/traccar/helper/PasswordHash.java')
-rw-r--r--src/org/traccar/helper/PasswordHash.java231
1 files changed, 231 insertions, 0 deletions
diff --git a/src/org/traccar/helper/PasswordHash.java b/src/org/traccar/helper/PasswordHash.java
new file mode 100644
index 000000000..98ded0988
--- /dev/null
+++ b/src/org/traccar/helper/PasswordHash.java
@@ -0,0 +1,231 @@
+package org.traccar.helper;
+
+/*
+ * Password Hashing With PBKDF2 (http://crackstation.net/hashing-security.htm).
+ * Copyright (c) 2013, Taylor Hornby
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+import java.security.SecureRandom;
+
+import javax.crypto.spec.PBEKeySpec;
+import javax.crypto.SecretKeyFactory;
+
+import java.math.BigInteger;
+import java.security.NoSuchAlgorithmException;
+import java.security.spec.InvalidKeySpecException;
+
+/*
+ * PBKDF2 salted password hashing.
+ * Author: havoc AT defuse.ca
+ * www: http://crackstation.net/hashing-security.htm
+ */
+public class PasswordHash {
+ public static final String PBKDF2_ALGORITHM = "PBKDF2WithHmacSHA1";
+
+ // The following constants may be changed without breaking existing hashes.
+ public static final int SALT_BYTE_SIZE = 24;
+ public static final int HASH_BYTE_SIZE = 24;
+ public static final int PBKDF2_ITERATIONS = 1000;
+
+ public static final int ITERATION_INDEX = 0;
+ public static final int SALT_INDEX = 1;
+ public static final int PBKDF2_INDEX = 2;
+
+ public static class HashingResult {
+
+ public final Integer iterations;
+ public final String hash;
+ public final String salt;
+
+ public HashingResult(Integer iterations, String hash, String salt) {
+ this.hash = hash;
+ this.salt = salt;
+ this.iterations = iterations;
+ }
+ }
+
+ /**
+ * Returns a salted PBKDF2 hash of the password.
+ *
+ * @param password
+ * the password to hash
+ * @return a salted PBKDF2 hash of the password
+ */
+ public static HashingResult createHash(String password) {
+ return createHash(password.toCharArray());
+ }
+
+ /**
+ * Returns a salted PBKDF2 hash of the password.
+ *
+ * @param password
+ * the password to hash
+ * @return a salted PBKDF2 hash of the password
+ */
+ public static HashingResult createHash(char[] password) {
+ // 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);
+
+ return new HashingResult(PBKDF2_ITERATIONS, toHex(hash), toHex(salt));
+ }
+
+ /**
+ * Validates a password using a hash.
+ *
+ * @param password
+ * the password to check
+ * @param correctHash
+ * the hash of the valid password
+ * @return true if the password is correct, false if not
+ */
+ public static boolean validatePassword(String password, String correctHash)
+ throws NoSuchAlgorithmException, InvalidKeySpecException {
+ return validatePassword(password.toCharArray(), correctHash);
+ }
+
+ /**
+ * Validates a password using a hash.
+ *
+ * @param password
+ * the password to check
+ * @param correctHash
+ * the hash of the valid password
+ * @return true if the password is correct, false if not
+ */
+ public static boolean validatePassword(char[] password, String correctHash)
+ throws NoSuchAlgorithmException, InvalidKeySpecException {
+ // Decode the hash into its parameters
+ String[] params = correctHash.split(":");
+ int iterations = Integer.parseInt(params[ITERATION_INDEX]);
+ return validatePassword(password, iterations, params[SALT_INDEX], params[PBKDF2_INDEX]);
+ }
+
+ public static boolean validatePassword(char[] password, int iterations,
+ String saltHex, String hashHex) {
+ byte[] salt = fromHex(saltHex);
+ byte[] hash = fromHex(hashHex);
+ // 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 hashes in constant time. The password is correct if
+ // both hashes match.
+ return slowEquals(hash, testHash);
+ }
+
+
+
+
+ public static boolean slowEquals(String hash1, String hash2) {
+ return slowEquals(fromHex(hash1), fromHex(hash2));
+ }
+
+ /**
+ * Compares two byte arrays in length-constant time. This comparison method
+ * is used so that password hashes cannot be extracted from an on-line
+ * system using a timing attack and then attacked off-line.
+ *
+ * @param a
+ * the first byte array
+ * @param b
+ * the second byte array
+ * @return true if both byte arrays are the same, false if not
+ */
+ 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;
+ }
+
+ /**
+ * Computes the PBKDF2 hash of a password.
+ *
+ * @param password
+ * the password to hash.
+ * @param salt
+ * the salt
+ * @param iterations
+ * the iteration count (slowness factor)
+ * @param bytes
+ * the length of the hash to compute in bytes
+ * @return the PBDKF2 hash of the password
+ */
+ private static byte[] pbkdf2(char[] password, byte[] salt, int iterations,
+ int bytes) {
+ try {
+ PBEKeySpec spec = new PBEKeySpec(password, salt, iterations,
+ bytes * 8);
+ SecretKeyFactory skf = SecretKeyFactory
+ .getInstance(PBKDF2_ALGORITHM);
+ return skf.generateSecret(spec).getEncoded();
+ } catch (NoSuchAlgorithmException e) {
+ throw new RuntimeException(e);
+ } catch (InvalidKeySpecException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * Converts a string of hexadecimal characters into a byte array.
+ *
+ * @param hex
+ * the hex string
+ * @return the hex string decoded into a byte array
+ */
+ private static byte[] fromHex(String hex) {
+ byte[] binary = new byte[hex.length() / 2];
+ for (int i = 0; i < binary.length; i++) {
+ binary[i] = (byte) Integer.parseInt(
+ hex.substring(2 * i, 2 * i + 2), 16);
+ }
+ return binary;
+ }
+
+ /**
+ * Converts a byte array into a hexadecimal string.
+ *
+ * @param array
+ * the byte array to convert
+ * @return a length*2 character string encoding the byte array
+ */
+ private static String toHex(byte[] array) {
+ 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;
+ }
+
+
+
+} \ No newline at end of file