You need to enable JavaScript to run this app. Java Android中PBKDF2密码哈希登录校验不匹配问题排查
优惠活动
大模型
产品
解决方案
定价
更多

Java Android中PBKDF2密码哈希登录校验不匹配问题排查

问题根因与修复方案

核心逻辑错误

  • 盐值未持久化,每次计算随机生成新值
    你实现的PBKDF2_Encrypt方法,每次调用都会新生成一个独立的16字节随机盐,计算完哈希后没有留存盐值、也没有和哈希结果一起存储。
    首次设置密码流程:生成随机盐A → 用盐A计算得到哈希H1 → 仅将H1存入SharedPreferences,盐A直接丢弃
    登录校验流程:重新随机生成盐B → 用盐B计算输入密码的哈希H2 → 由于两次计算使用的盐完全不同,哪怕输入密码和设置时完全一致,H2永远不可能和存储的H1相等
  • 二进制内容转字符串的编码方式错误
    你直接将哈希得到的二进制字节数组按UTF-8编码转为字符串,大量二进制字节不属于合法UTF-8字符序列,转换过程中会出现替换字符、字节丢失,哪怕盐值完全一致,存储的字符串内容也会出现损坏,无法正确比对。
  • 冗余的存储数据清理逻辑
    FirstTimeActivity的onCreate方法中,硬编码了删除isFirstTime、PasswordHash两个存储字段的逻辑,只要页面被启动,已经写入的有效密码数据就会被直接清空,相当于每次打开首次设置页就会把之前存的所有密码相关数据擦掉。

正确实现规范

  1. 首次设置密码阶段
    生成随机盐值后,必须将盐值和计算得到的哈希结果绑定后整体存储,不能只单独存哈希值。
  2. 登录校验阶段
    从存储中读取之前绑定好的盐值与哈希结果,使用已存储的原有盐值,对用户输入的密码重新计算PBKDF2哈希,再将新算出的哈希和原有存储的哈希做比对,不能每次校验都新生成随机盐。
  3. 编码处理要求
    所有二进制格式的盐、哈希内容,统一通过Base64编码转为可安全存储的字符串,避免不同字符集转换带来的内容损坏。

可直接复用的修复后工具方法

// 首次设置密码时调用:返回Base64编码的「盐+哈希」组合字符串,直接存入SharedPreferences即可
public static String generateHashWithSalt(String password) throws NoSuchAlgorithmException, InvalidKeySpecException {
    SecureRandom random = new SecureRandom();
    byte[] salt = new byte[16];
    random.nextBytes(salt);
    KeySpec spec = new PBEKeySpec(password.toCharArray(), salt, 65536, 128);
    SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
    byte[] hash = factory.generateSecret(spec).getEncoded();
    // 拼接盐和哈希为完整字节数组,整体做Base64编码
    ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
    outputStream.write(salt, 0, salt.length);
    outputStream.write(hash, 0, hash.length);
    return Base64.encodeToString(outputStream.toByteArray(), Base64.NO_WRAP);
}

// 登录校验时调用:传入存储的组合字符串、用户输入的密码,返回校验是否通过
public static boolean verifyPassword(String storedCombination, String inputPassword) throws NoSuchAlgorithmException, InvalidKeySpecException {
    byte[] combinationBytes = Base64.decode(storedCombination, Base64.NO_WRAP);
    // 从存储的组合字节中拆分出原有盐和原始哈希值
    byte[] salt = Arrays.copyOfRange(combinationBytes, 0, 16);
    byte[] originalHash = Arrays.copyOfRange(combinationBytes, 16, combinationBytes.length);
    KeySpec spec = new PBEKeySpec(inputPassword.toCharArray(), salt, 65536, 128);
    SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
    byte[] inputHash = factory.generateSecret(spec).getEncoded();
    // 使用固定时间比对,避免时序泄漏风险
    return MessageDigest.isEqual(originalHash, inputHash);
}

额外提醒:SharedPreferences存储敏感数据的安全风险你已知晓,这里不再重复提示。


内容的提问来源于stack exchange,提问作者Cpt Price

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.27 12:57:03