在 Android 上检查用户密码
Checking user password on Android
我正在为我正在开发的 Android 应用编写一个实用程序 class 来确保密码安全。目前,它生成一个盐,然后使用该盐和密码作为参数生成一个散列。我需要一种方法,将存储在数据库中的哈希与用户尝试登录时创建的哈希进行比较。我应该使用 Arrays.equals()
比较两个字节数组吗?
或者我应该将 byte[] dbHash
、String password
和 byte[] 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;
}
我正在为我正在开发的 Android 应用编写一个实用程序 class 来确保密码安全。目前,它生成一个盐,然后使用该盐和密码作为参数生成一个散列。我需要一种方法,将存储在数据库中的哈希与用户尝试登录时创建的哈希进行比较。我应该使用 Arrays.equals()
比较两个字节数组吗?
或者我应该将 byte[] dbHash
、String password
和 byte[] 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;
}