使用Bouncy Castle实现PBKDF2时哈希重复问题求助
问题
尝试用PBKDF2+Bouncy Castle实现密码哈希函数,每次输入同一密码生成的哈希却完全相同,明明每次都用SecureRandom生成了新盐。代码如下:
SecureRandom random = new SecureRandom(); byte[] salt = new byte[8]; final int iterationCount = 20000; final int keyLength = 256; random.nextBytes(salt); try { SecretKeyFactory factoryBC = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA3-256", "BC"); KeySpec keySpecBC = new PBEKeySpec(password.toCharArray(), salt, iterationCount, keyLength); SecretKey keyBC = factoryBC.generateSecret(keySpecBC); String hashedPW = keyBC.getEncoded().toString(); }
比如输入“hello”,每次得到的都是[B@44af588b。
问题原因
核心问题出在keyBC.getEncoded().toString()这行代码:
keyBC.getEncoded()返回的是字节数组,但Java中字节数组的toString()方法不会把字节内容转成可读字符串,它返回的是[B@加上数组对象的哈希码,这个值和数组里的实际哈希内容完全无关。- 你看到的
[B@44af588b只是数组对象的标识,不是真正的密码哈希值,所以不管盐怎么变化,这个标识都可能重复(或仅在不同运行环境下改变,但本质和哈希无关)。
修复方案
- 正确转换字节数组为字符串:用Base64编码把哈希字节数组转成可读字符串(也可以用十六进制,Base64格式更紧凑)。
- 保存盐:哈希验证时需要用到相同的盐,所以生成哈希时要把盐和最终哈希值一起存储(比如用分隔符拼接,或者分开存入数据库字段)。
修复后的代码示例:
SecureRandom random = new SecureRandom(); byte[] salt = new byte[8]; final int iterationCount = 20000; final int keyLength = 256; random.nextBytes(salt); try { SecretKeyFactory factoryBC = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA3-256", "BC"); KeySpec keySpecBC = new PBEKeySpec(password.toCharArray(), salt, iterationCount, keyLength); SecretKey keyBC = factoryBC.generateSecret(keySpecBC); byte[] hashBytes = keyBC.getEncoded(); // 将哈希字节数组转成Base64字符串 String hashedPW = Base64.getEncoder().encodeToString(hashBytes); // 将盐也转成Base64字符串,和哈希拼接后存储 String storedValue = Base64.getEncoder().encodeToString(salt) + ":" + hashedPW; } catch (NoSuchAlgorithmException | InvalidKeySpecException e) { e.printStackTrace(); }
哈希验证思路
验证时需要从存储内容中拆分出盐和哈希值:
- 按存储时用的分隔符拆分字符串,得到盐的Base64字符串和哈希的Base64字符串。
- 将盐的Base64字符串解码回字节数组。
- 用相同的PBKDF2参数(输入密码、盐、迭代次数、密钥长度)重新生成哈希。
- 用
Arrays.equals()比较新生成的哈希字节数组和存储的哈希字节数组(或转成字符串后比较)。
内容的提问来源于stack exchange,提问作者Jeremy Romano
相关产品推荐
相关产品推荐

