diff options
Diffstat (limited to 'src/main/java/org/traccar/helper/Hashing.java')
-rw-r--r-- | src/main/java/org/traccar/helper/Hashing.java | 100 |
1 files changed, 100 insertions, 0 deletions
diff --git a/src/main/java/org/traccar/helper/Hashing.java b/src/main/java/org/traccar/helper/Hashing.java new file mode 100644 index 000000000..e91310eda --- /dev/null +++ b/src/main/java/org/traccar/helper/Hashing.java @@ -0,0 +1,100 @@ +/* + * Copyright 2015 Anton Tananaev (anton@traccar.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.traccar.helper; + +import javax.crypto.SecretKeyFactory; +import javax.crypto.spec.PBEKeySpec; +import java.security.NoSuchAlgorithmException; +import java.security.SecureRandom; +import java.security.spec.InvalidKeySpecException; + +public final class Hashing { + + public static final int ITERATIONS = 1000; + public static final int SALT_SIZE = 24; + public static final int HASH_SIZE = 24; + + private static SecretKeyFactory factory; + static { + try { + factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1"); + } catch (NoSuchAlgorithmException e) { + e.printStackTrace(); + } + } + + public static class HashingResult { + + private final String hash; + private final String salt; + + public HashingResult(String hash, String salt) { + this.hash = hash; + this.salt = salt; + } + + public String getHash() { + return hash; + } + + public String getSalt() { + return salt; + } + } + + private Hashing() { + } + + private static byte[] function(char[] password, byte[] salt) { + try { + PBEKeySpec spec = new PBEKeySpec(password, salt, ITERATIONS, HASH_SIZE * Byte.SIZE); + return factory.generateSecret(spec).getEncoded(); + } catch (InvalidKeySpecException e) { + throw new SecurityException(e); + } + } + + private static final SecureRandom RANDOM = new SecureRandom(); + + public static HashingResult createHash(String password) { + byte[] salt = new byte[SALT_SIZE]; + RANDOM.nextBytes(salt); + byte[] hash = function(password.toCharArray(), salt); + return new HashingResult( + DataConverter.printHex(hash), + DataConverter.printHex(salt)); + } + + public static boolean validatePassword(String password, String hashHex, String saltHex) { + byte[] hash = DataConverter.parseHex(hashHex); + byte[] salt = DataConverter.parseHex(saltHex); + return slowEquals(hash, function(password.toCharArray(), salt)); + } + + /** + * 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. + */ + 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; + } + +} |