使用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
相关产品推荐
相关产品推荐

