You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用Password4J的Argon2哈希相同输入结果不一致问题求助

Password4J Argon2哈希重复计算结果不一致问题排查求助

我写了一个简单的Hash类来学习Password4J库,现在遇到了一个问题:使用相同明文调用Password.hash(...),第一次用自动生成的盐并保存,第二次使用该保存的盐,但得到的哈希密码字节却不相同。如果有使用该库进行Argon2哈希的经验,麻烦提供排查思路或建议,帮我找出问题所在。

ps24j.properties配置(已确认库加载了这些值)

# Uncomment when everything's working
#global.banner=false

# Silly, I know. But let's have some fun. Let your hair down a little, huh?!
global.pepper=NowIsTheTimeForAllGoodMenToComeToTheAidOfTheirCountry

global.random.strong=false


# Amount of memory (in kibibytes) to use.
# 64 mb
hash.argon2.memory=65536

# Number of iterations to perform.
hash.argon2.iterations=3

# Degree of parallelism (number of threads to be used in the computation).
hash.argon2.parallelism=4

# Desired length of the final derived key. Using 16 for easier manual visual compare
hash.argon2.length=16

# Desired type of the algorithm. Possible values are d for Argon2d, i for Argon2i, or id for Argon2id.
hash.argon2.type=id

# Defines the version of the algorithm to use.
hash.argon2.version=19

哈希生成代码(已确认pepper与配置文件一致;HashResults仅包含hash和salt的byte[])

public HashResults generateHash( String clearText )
{
    String pepper = PepperGenerator.get();
    Hash hash = Password
            .hash( clearText )
            .addRandomSalt()
            .addPepper( pepper )
            .withArgon2();

    return new HashResults()
            .setSaltBytes( hash.getSaltBytes() )
            .setHashBytes( hash.getResultAsBytes() );
}

public HashResults generateHash( String clearText, byte[] salt )
{
    String pepper = PepperGenerator.get();
    Hash hash = Password
            .hash( clearText )
            .addSalt( salt )
            .addPepper( pepper )
            .withArgon2();

    return new HashResults()
            .setSaltBytes( hash.getSaltBytes() )
            .setHashBytes( hash.getResultAsBytes() );
}

测试代码

HashResults hashResults = hashing.generateHash( "b00ya" );
System.out.println("HASH0: " + Arrays.toString( hashResults.getHashBytes() ) );
System.out.println("SALT0: " + Arrays.toString( hashResults.getSaltBytes() ) );

HashResults hashResults2 = hashing.generateHash( "b00ya", hashResults.getSaltBytes() );
System.out.println("HASH1: " + Arrays.toString( hashResults2.getHashBytes() ) );
System.out.println("SALT1: " + Arrays.toString( hashResults2.getSaltBytes() ) );

测试结果

HASH0: [36, 97, 114, 103, 111, 110, 50, 105, 100, 36, 118, 61, 49, 57, 36, 109, 61, 54, 53, 53, 51, 54, 44, 116, 61, 51, 44, 112, 61, 52, 36, 86, 69, 98, 51, 90, 111, 114, 114, 73, 102, 107, 66, 48, 119, 115, 83, 65, 86, 113, 57, 119, 89, 116, 72, 81, 98, 117, 112, 75, 122, 53, 111, 101, 122, 71, 109, 56, 66, 48, 101, 108, 121, 85, 98, 112, 104, 68, 47, 69, 106, 88, 57, 116, 71, 81, 78, 51, 78, 43, 110, 53, 82, 105, 114, 106, 114, 107, 69, 82, 120, 52, 74, 80, 112, 43, 120, 56, 83, 77, 71, 107, 107, 98, 67, 103, 103, 36, 49, 83, 81, 54, 78, 50, 68, 113, 79, 109, 43, 99, 101, 97, 84, 111, 77, 80, 82, 50, 79, 65]
HASH1: [36, 97, 114, 103, 111, 110, 50, 105, 100, 36, 118, 61, 49, 57, 36, 109, 61, 54, 53, 53, 51, 54, 44, 116, 61, 51, 44, 112, 61, 52, 36, 86, 69, 98, 118, 118, 55, 49, 109, 55, 55, 43, 57, 55, 55, 43, 57, 73, 101, 43, 47, 118, 81, 72, 118, 118, 55, 48, 76, 69, 103, 70, 97, 55, 55, 43, 57, 55, 55, 43, 57, 55, 55, 43, 57, 82, 48, 72, 118, 118, 55, 51, 118, 118, 55, 48, 114, 80, 109, 104, 55, 77, 101, 43, 47, 118, 101, 43, 47, 118, 82, 48, 101, 55, 55, 43, 57, 74, 82, 118, 118, 118, 55, 48, 81, 55, 55, 43, 57, 69, 106, 88, 118, 118, 55, 51, 118, 118, 55, 49, 107, 68, 101, 43, 47, 118, 100, 43, 110, 55, 55, 43, 57, 71, 79, 43, 47, 118, 101, 43, 47, 118, 101, 43, 47, 118, 81, 82, 72, 72, 103, 107, 43, 55, 55, 43, 57, 55, 55, 43, 57, 55, 55, 43, 57, 73, 119, 98, 118, 118, 55, 49, 71, 119, 111, 73, 36, 112, 104, 48, 54, 105, 74, 112, 82, 88, 117, 118, 66, 89, 121, 67, 82, 113, 51, 69, 89, 116, 65]

SALT0: [84, 70, -17, -65, -67, 102, -17, -65, -67, -17, -65, -67, 33, -17, -65, -67, 1, -17, -65, -67, 11, 18, 1, 90, -17, -65, -67, -17, -65, -67, -17, -65, -67, 71, 65, -17, -65, -67, -17, -65, -67, 43, 62, 104, 123, 49, -17, -65, -67, -17, -65, -67, 29, 30, -17, -65, -67, 37, 27, -17, -65, -67, 16, -17, -65, -67, 18, 53, -17, -65, -67, -17, -65, -67, 100, 13, -17, -65, -67, -33, -89, -17, -65, -67, 24, -17, -65, -67, -17, -65, -67, -17, -65, -67, 4, 71, 30, 9, 62, -17, -65, -67, -17, -65, -67, -17, -65, -67, 35, 6, -17, -65, -67, 70, -62, -126]
SALT1: [84, 70, -17, -65, -67, 102, -17, -65, -67, -17, -65, -67, 33, -17, -65, -67, 1, -17, -65, -67, 11, 18, 1, 90, -17, -65, -67, -17, -65, -67, -17, -65, -67, 71, 65, -17, -65, -67, -17, -65, -67, 43, 62, 104, 123, 49, -17, -65, -67, -17, -65, -67, 29, 30, -17, -65, -67, 37, 27, -17, -65, -67, 16, -17, -65, -67, 18, 53, -17, -65, -67, -17, -65, -67, 100, 13, -17, -65, -67, -33, -89, -17, -65, -67, 24, -17, -65, -67, -17, -65, -67, -17, -65, -67, 4, 71, 30, 9, 62, -17, -65, -67, -17, -65, -67, -17, -65, -67, 35, 6, -17, -65, -67, 70, -62, -126]

排查思路建议

  • 确认Pepper的绝对一致性:虽然你说已确认pepper与配置一致,但要严格验证PepperGenerator.get()每次调用返回的字符串完全相同——比如在两次哈希生成前分别打印pepper的字节数组,确保没有动态生成或编码差异。
  • 区分原始派生密钥与编码后的哈希字符串:你配置的hash.argon2.length=16是原始派生密钥的长度,但getResultAsBytes()返回的是包含算法参数、盐、哈希值的完整编码字符串的字节(比如开头的$argon2id$v=19$...格式)。第二次调用时,即使盐相同,若编码过程中存在参数拼接差异(比如隐式参数不同),也会导致结果不同。建议改用hash.getBytes()获取原始16字节派生密钥,再对比是否一致。
  • 验证两次调用的Argon2参数完全一致:检查第二次调用withArgon2()时,是否继承了配置中的所有参数(memory、iterations、parallelism、version、type),有没有被代码中的其他逻辑覆盖。可以在两次生成哈希后打印hash.getParameters()对比。
  • 严格对比盐的字节数组:虽然控制台打印的SALT0和SALT1看起来一样,但用Arrays.equals(hashResults.getSaltBytes(), hashResults2.getSaltBytes())做严格校验,确保传递过程中没有字节数组被修改、截断或编码错误。
  • 使用Password4J自带的验证方法:不要手动对比哈希字节,改用库自带的验证逻辑:Password.verify("b00ya", new String(hashResults.getHashBytes())).withArgon2(),该方法会自动解析哈希字符串中的所有参数(盐、算法配置等)并重新计算验证,避免手动处理的误差。

内容的提问来源于stack exchange,提问作者Dave Lowe

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.30 09:55:55