package io.fathom.cloud; import io.fathom.cloud.protobuf.CloudCommons.PasswordHashData; import java.security.NoSuchAlgorithmException; import java.security.spec.InvalidKeySpecException; import javax.crypto.SecretKeyFactory; import javax.crypto.interfaces.PBEKey; import javax.crypto.spec.PBEKeySpec; import javax.inject.Singleton; import com.google.protobuf.ByteString; @Singleton public class PasswordHasher { public static final int DEFAULT_ITERATION_COUNT = 1000; public static final int DEFAULT_KEYSIZE = 128; public static final int DEFAULT_SALT_LENGTH = 128; public static PBEKey doPbkdf2(int iterationCount, byte[] salt, String password, int keyLength) { PBEKeySpec pbeKeySpec = new PBEKeySpec(password.toCharArray(), salt, iterationCount, keyLength); SecretKeyFactory factory; try { factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1"); } catch (NoSuchAlgorithmException e) { throw new IllegalStateException("Unable to get PBKDF2 provider", e); } PBEKey key; try { key = (PBEKey) factory.generateSecret(pbeKeySpec); } catch (InvalidKeySpecException e) { throw new IllegalStateException("Error generating secret", e); } return key; } public boolean isValid(PasswordHashData passwordHash, String password) { byte[] salt = passwordHash.getSalt().toByteArray(); int rounds = passwordHash.getRounds(); int keySize = DEFAULT_KEYSIZE; PBEKey key = doPbkdf2(rounds, salt, password, keySize); byte[] encoded = key.getEncoded(); byte[] stored = passwordHash.getData().toByteArray(); return org.keyczar.util.Util.safeArrayEquals(encoded, stored); } public PasswordHashData hash(String password) { PasswordHashData.Builder hasher = PasswordHashData.newBuilder(); byte[] salt = org.keyczar.util.Util.rand(DEFAULT_SALT_LENGTH / 8); int rounds = DEFAULT_ITERATION_COUNT; int keySize = DEFAULT_KEYSIZE; hasher.setSalt(ByteString.copyFrom(salt)); hasher.setRounds(rounds); PBEKey key = doPbkdf2(rounds, salt, password, keySize); byte[] encoded = key.getEncoded(); hasher.setData(ByteString.copyFrom(encoded)); return hasher.build(); } }