pbkdf2 pkcs5兼容Java、PHP和Objective中的哈希
我正在开发一个需要pbkdf2 pkcs5进行密码哈希的应用程序。我想我有一些PHP的工作代码&;Java,但我不确定它们为什么会产生不同的结果
这是代码
Php代码:
function hash_password($password, $salt, $user_id, $iterations) {
$user_id = (string) $user_id;
$raw_pass = $salt . $user_id . $password;
$hash = hash_pbkdf2("sha512", $raw_pass, $salt, $iterations, 64, false);
return $hash;
}
echo hash_password("password", $salt, 2058, 1);
echo PHP_EOL;
Java代码:
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
import java.nio.charset.StandardCharsets;
import java.lang.RuntimeException;
import java.security.NoSuchAlgorithmException;
import java.security.spec.InvalidKeySpecException;
import java.math.BigInteger;
import javax.xml.bind.DatatypeConverter;
public final class Testing {
private static String mSalt = "fd6c3dc0165dc420b4e9225bc9bee9684387e9621b2e2c00cfffebf1ec7c30b4";
private static String hashPassword(String password, String salt, int userId, int iterations) throws RuntimeException {
String uid = String.valueOf(userId);
StringBuilder pwBuilder = new StringBuilder(salt);
pwBuilder.append(uid);
pwBuilder.append(password);
String rawPass = pwBuilder.toString();
try {
SecretKeyFactory skf = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA512");
PBEKeySpec spec = new PBEKeySpec(password.toCharArray(), mSalt.getBytes(), iterations, 256);
byte[] result = skf.generateSecret(spec).getEncoded();
StringBuilder hashBuilder = new StringBuilder(result.length * 2);
for (byte r : result) {
hashBuilder.append(String.format("%02x", r));
}
return hashBuilder.toString();
} catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
throw new RuntimeException( e );
}
}
public static void main(String[] args) {
System.out.println(hashPassword("password", mSalt, 2058, 1000000));
System.out.println("\n");
}
}
Php输出为:3754036ea2f37af8160e45d15c2e4d6597157ead118f060e76152c0813f806e8
Java输出是:8b135bb19263677b70f1d5cbda5a23bc16df38e5e1a624c6df9a462975cac899
你能推荐一个既有兼容的实现又有gist的库吗
# 1 楼答案
现有代码存在两个问题:
使用相同的迭代次数
使用相同的串联密码
在PHP中:
使用
而不是
在Java中:
使用
而不是
进行这些更改后,两者都将产生相同的结果:
其他问题:
不要在没有特定字符集的情况下使用
String#getBytes
。否则,将使用不同JVM中可能不同的系统默认值。如果使用PBKDF2散列密码,那么在验证重新输入的密码时,需要比较两个散列。这需要一个恒定的时间比较函数来防止基于时间的攻击