Java、PHP 和 Objective-C 中的 pbkdf2 pkcs5 兼容散列

Posted

技术标签:

【中文标题】Java、PHP 和 Objective-C 中的 pbkdf2 pkcs5 兼容散列【英文标题】:pbkdf2 pkcs5 compatible hashing in Java, PHP and Objective-C 【发布时间】:2018-01-15 06:22:56 【问题描述】:

我正在开发一个需要 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

你能推荐一个同时兼容实现的库或要点吗?

【问题讨论】:

因为它是PBKDF2WithHmacSHA512,其中PBKDF2 表示基于密码的密钥派生函数2,在您的情况下是伪随机函数。您的 Java 代码将始终产生不同的哈希值,但是 sha512 将始终为您提供相同的结果。您不必担心哈希值不同,只需担心check 函数,它将确认密码的有效性。 @Axalix 我试过SecretKeyFactory.getInstance("SHA512");,但它抛出了NoSuchAlgorithmException 【参考方案1】:

现有代码存在两个问题:

    使用相同的迭代次数。

    使用相同的串联密码。

在 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 对密码进行哈希处理,则在验证重新输入的密码时需要比较两个哈希值。这需要一个恒定时间比较函数来防止基于时间的攻击。

【讨论】:

以上是关于Java、PHP 和 Objective-C 中的 pbkdf2 pkcs5 兼容散列的主要内容,如果未能解决你的问题,请参考以下文章

PHP&Java&Objective-C互通DES加密

什么是objective-c中的php array()等价物

在Objective-C中浅谈面向对象

Objective-C中的委托(代理)模式

8月语言排行:Objective-C走向衰败?我反对

如何共享/分发已编译的Objective-C代码(如Java中的JAR)?