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

使用Rfc2898DeriveBytes与MSSQL的C#登录系统密码验证异常求助

密码验证不稳定问题排查与解决

你的密码验证时灵时不灵的核心问题,出在错误地用Unicode编码将字节数组(哈希值、salt)转换为字符串存储——这会直接破坏原始字节数据,导致后续验证时无法还原正确的字节序列,自然会出现匹配失败的情况。

问题根源详解

不是所有字节序列都对应有效的Unicode字符。当你用Encoding.Unicode.GetString(byte[])转换字节数组时,无效的字节序列会被自动替换成默认的占位字符(比如�),这就永久性丢失了原始字节信息。把这种失真的字符串存入数据库,再取出来转成字节数组时,已经和最初的哈希、salt字节完全不一样了,验证失败也就成了大概率事件。你后面加的字符串对比偶尔成功只是巧合,根本解决不了本质问题。

修复方案:用Base64编码存储字节数据

Base64是专门为将任意字节序列转换为可打印字符串设计的标准编码,不会丢失任何原始信息,完全适合存储哈希值和salt。

1. 修改新增用户的存储逻辑

把原来的Unicode转换替换成Base64编码:

public SecurityReturnMessage AddUser(string username, string password) {
    byte[] salt = PasswordManagement.GenerateSalt();
    byte[] hashedPassword = PasswordManagement.HashPassword(Encoding.Unicode.GetBytes(password), salt);
    
    // 用Base64替代Unicode转换字节数组
    string hashPwdBase64 = Convert.ToBase64String(hashedPassword);
    string saltBase64 = Convert.ToBase64String(salt);
    
    using (var conn = new SqlConnection(yourConnectionString)) // 建议用using自动管理连接
    {
        conn.Open();
        using (IDbCommand comm = conn.CreateCommand()) {
            comm.CommandText = "usp_IFRS_SEC_USER_INSERT";
            comm.CommandType = CommandType.StoredProcedure;
            
            SqlParameter hashPwd = new SqlParameter("@hashpwd", SqlDbType.NVarChar) { Value = hashPwdBase64 };
            SqlParameter saltParameter = new SqlParameter("@salt", SqlDbType.NVarChar) { Value = saltBase64 };
            
            comm.Parameters.Add(Extensions.CreateParameter(comm, "@user", username));
            comm.Parameters.Add(hashPwd);
            comm.Parameters.Add(saltParameter);
            
            var returnVal = comm.CreateParameter();
            returnVal.Direction = ParameterDirection.ReturnValue;
            comm.Parameters.Add(returnVal);
            
            comm.ExecuteNonQuery();
            int result = (int)returnVal.Value;
            return (SecurityReturnMessage)result;
        }
    }
}

2. 修改密码验证逻辑

从数据库取出Base64字符串后,转回字节数组再对比:

public static bool IsPasswordValid(string inputPassword, string hashedPasswordBase64, string saltBase64) {
    // 将Base64字符串还原为原始字节数组
    byte[] salt = Convert.FromBase64String(saltBase64);
    byte[] storedHashedPassword = Convert.FromBase64String(hashedPasswordBase64);
    
    byte[] inputHashedPassword = HashPassword(Encoding.Unicode.GetBytes(inputPassword), salt);
    
    // 只需要对比字节数组即可,无需冗余的字符串对比
    return storedHashedPassword.SequenceEqual(inputHashedPassword);
}

3. 数据库字段优化建议

确保hashpwd和salt字段的长度足够存储Base64字符串:32字节的原始数据转成Base64后是44字符左右,所以字段长度设为50或100即可,避免字符串被截断。

内容的提问来源于stack exchange,提问作者Robert Woods

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.15 03:49:12