diff options
Diffstat (limited to 'src/org/traccar/helper/Hashing.java')
-rw-r--r-- | src/org/traccar/helper/Hashing.java | 82 |
1 files changed, 70 insertions, 12 deletions
diff --git a/src/org/traccar/helper/Hashing.java b/src/org/traccar/helper/Hashing.java index 3f3e30e6e..000821451 100644 --- a/src/org/traccar/helper/Hashing.java +++ b/src/org/traccar/helper/Hashing.java @@ -1,21 +1,79 @@ +/* + * Copyright 2015 Anton Tananaev (anton.tananaev@gmail.com) + * + * 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 java.io.UnsupportedEncodingException; -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; +import javax.crypto.SecretKeyFactory; +import javax.crypto.spec.PBEKeySpec; +import java.security.GeneralSecurityException; +import java.security.SecureRandom; public class Hashing { - - public static byte[] sha256(String text) { + + public static final int ITERATIONS = 1000; + public static final int SALT_SIZE = 24; + public static final int HASH_SIZE = 24; + + public static class HashingResult { + + public final String hash; + public final String salt; + + public HashingResult(String hash, String salt) { + this.hash = hash; + this.salt = salt; + } + } + + private static byte[] function(char[] password, byte[] salt) { try { - MessageDigest md = MessageDigest.getInstance("SHA-256"); - md.update(text.getBytes("UTF-8")); - return md.digest(); - } catch (NoSuchAlgorithmException e) { - throw new RuntimeException(e); - } catch (UnsupportedEncodingException e) { - throw new RuntimeException(e); + PBEKeySpec spec = new PBEKeySpec(password, salt, ITERATIONS, HASH_SIZE * Byte.SIZE); + SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1"); + return factory.generateSecret(spec).getEncoded(); + } catch (GeneralSecurityException error) { + throw new SecurityException(error); + } + } + + private static 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( + ChannelBufferTools.convertByteArray(hash), + ChannelBufferTools.convertByteArray(salt)); + } + + public static boolean validatePassword(String password, String hashHex, String saltHex) { + byte[] hash = ChannelBufferTools.convertHexString(hashHex); + byte[] salt = ChannelBufferTools.convertHexString(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; } } |