有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

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) 个答案

  1. # 1 楼答案

    现有代码存在两个问题:

    1. 使用相同的迭代次数

    2. 使用相同的串联密码

    在PHP中:

    使用

    var_dump(hash_password("password", $salt, 2058, 1000000));
    

    而不是

    var_dump(hash_password("password", $salt, 2058, 1));
    

    在Java中:

    使用

    PBEKeySpec spec = new PBEKeySpec(rawPass.toCharArray(), mSalt.getBytes(), iterations, 256);
    

    而不是

    PBEKeySpec spec = new PBEKeySpec(password.toCharArray(), mSalt.getBytes(), iterations, 256);
    

    进行这些更改后,两者都将产生相同的结果:

    424a0b30ea0cdef2ff0185f49ce67ee44273ce7e41026044d527c85550b3a207
    

    其他问题:

    • 不要在没有特定字符集的情况下使用String#getBytes。否则,将使用不同JVM中可能不同的系统默认值。

    • 如果使用PBKDF2散列密码,那么在验证重新输入的密码时,需要比较两个散列。这需要一个恒定的时间比较函数来防止基于时间的攻击