在 Android 上检查用户密码

Checking user password on Android

我正在为我正在开发的 Android 应用编写一个实用程序 class 来确保密码安全。目前,它生成一个盐,然后使用该盐和密码作为参数生成一个散列。我需要一种方法,将存储在数据库中的哈希与用户尝试登录时创建的哈希进行比较。我应该使用 Arrays.equals() 比较两个字节数组吗? 或者我应该将 byte[] dbHashString passwordbyte[] salt 作为参数并从那里开始?

这是目前为止的代码。

package fitfast.security;

import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.spec.KeySpec;
import java.util.Arrays;

import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;

public final class Authenticator {

    private static final int length = 512;
    private static final int iterations = 60000;

    public static byte[] generateHash(String password, byte[] salt) throws NoSuchAlgorithmException, InvalidKeySpecException {
        String algorithm = "PBKDF2WithHmacSHA512";
        KeySpec sp = new PBEKeySpec(password.toCharArray(), salt, iterations, length);
        SecretKeyFactory kf = SecretKeyFactory.getInstance(algorithm);
        return kf.generateSecret(sp).getEncoded();
    }

    public static byte[] generateSalt() throws NoSuchAlgorithmException {
        SecureRandom sr = SecureRandom.getInstance("SHA1PRNG");
        byte[] salt = new byte[8];
        sr.nextBytes(salt);
        return salt;
    }

    public static boolean check(byte[] hash, String password, byte[] salt) {

        //code goes here

    }

}

我会说 Arrays.equals() 是正确的选择。像

public static boolean check(byte[] hash, String password, byte[] salt) {

   return Arrays.equals(hash, generateHash(password,salt));

}

虽然机​​会非常渺茫,但攻击者或许能够利用 Arrays.equals 比较两个哈希值的方式。一旦发现不同的字节,该函数就会迭代运行 returns。攻击者可能会尝试不同的密码并观察到不同的响应时间。这意味着哈希的第一个字节匹配(假设 PBKDF2 总是花费相同的时间)。这可以重复多次,直到暴力破解匹配的密码。

有多种方法可以以时间常数的方式比较数组。我喜欢对两个数组的每个字节进行异或运算,并将生成的字节数组进行或运算为单个字节。如果该字节为 0,则两个数组相等:

public static boolean check(byte[] hash, String password, byte[] salt) {
    byte[] generatedHash = generateHash(password,salt);
    byte result = (byte)0;
    for(int i = 0; i < hash.length; i++) {
        result |= hash[i] ^ generatedHash[i];
    }
    return result == (byte)0;
}