将Firebase用户迁移至ASP.NET:如何调用其自定义SCRYPT哈希算法
Firebase自定义SCRYPT哈希算法在ASP.NET(C#)中的实现方案
1. 提前准备必备参数
首先确认你已拿到两类配置参数:
- 单用户维度(从Firebase导出的用户数据中获取):
passwordHash:用户密码的Base64编码哈希结果salt:对应用户的加密盐值,Base64编码
- 项目全局维度(从Firebase CLI导出的哈希配置中获取):
- 签名密钥(signer key,Base64编码)
- 盐分隔符(salt separator,Base64编码,默认值为
Bw==) - 内存成本(mem_cost,默认值为14)
- 迭代次数(rounds,默认值为8)
2. C#核心验证逻辑实现
Firebase使用的是定制版SCRYPT算法,和标准SCRYPT的参数计算逻辑有差异,不能直接调用标准库默认方法,需按以下逻辑适配:
首先安装兼容.NET Core/.NET 5+的加密包:Install-Package CryptSharpStandard
完整验证代码示例:
using System; using System.Text; using System.Security.Cryptography; using CryptSharp; public static class FirebaseScryptValidator { // 处理Firebase导出的URL安全Base64编码 private static byte[] DecodeFirebaseBase64(string base64Str) { base64Str = base64Str.Replace('-', '+').Replace('_', '/'); switch (base64Str.Length % 4) { case 2: base64Str += "=="; break; case 3: base64Str += "="; break; } return Convert.FromBase64String(base64Str); } /// <summary> /// 验证用户输入密码是否和Firebase存储的哈希匹配 /// </summary> /// <param name="inputPassword">用户登录时输入的明文密码</param> /// <param name="storedSalt">数据库存储的对应用户salt</param> /// <param name="storedHash">数据库存储的对应用户passwordHash</param> /// <param name="signerKey">Firebase项目全局签名密钥</param> /// <param name="saltSeparator">Firebase项目全局盐分隔符</param> /// <param name="memCost">Firebase项目全局内存成本参数</param> /// <param name="rounds">Firebase项目全局迭代次数参数</param> public static bool VerifyPassword(string inputPassword, string storedSalt, string storedHash, string signerKey, string saltSeparator = "Bw==", int memCost = 14, int rounds = 8) { byte[] saltBytes = DecodeFirebaseBase64(storedSalt); byte[] separatorBytes = DecodeFirebaseBase64(saltSeparator); byte[] storedHashBytes = DecodeFirebaseBase64(storedHash); byte[] passwordBytes = Encoding.UTF8.GetBytes(inputPassword); // 拼接最终盐值:用户salt + 全局盐分隔符 byte[] finalSalt = new byte[saltBytes.Length + separatorBytes.Length]; Buffer.BlockCopy(saltBytes, 0, finalSalt, 0, saltBytes.Length); Buffer.BlockCopy(separatorBytes, 0, finalSalt, saltBytes.Length, separatorBytes.Length); // 计算SCRYPT派生哈希,Firebase固定派生密钥长度为32位 byte[] computedHash = SCrypt.ComputeDerivedKey( password: passwordBytes, salt: finalSalt, cost: 1 << memCost, // 内存成本为2的memCost次方 blockSize: 8, parallel: 1, maxThreads: 1, derivedKeyLength: 32 ); // 固定时间对比哈希,避免时序攻击 return CryptographicOperations.FixedTimeEquals(computedHash, storedHashBytes); } }
3. 集成到ASP.NET Identity体系
如果你的项目使用了ASP.NET Identity框架,可直接替换默认密码验证器,实现平滑迁移:
- 给用户表扩展两个字段:
FirebaseSalt(存储老用户的原始salt)、IsMigrated(标记用户是否已完成哈希算法迁移) - 实现自定义密码哈希器:
public class FirebaseCompatiblePasswordHasher : IPasswordHasher<ApplicationUser> { // 建议将Firebase配置存到appsettings.json中,不要硬编码 private readonly IConfiguration _configuration; private readonly IPasswordHasher<ApplicationUser> _defaultHasher = new PasswordHasher<ApplicationUser>(); public FirebaseCompatiblePasswordHasher(IConfiguration configuration) { _configuration = configuration; } public string HashPassword(ApplicationUser user, string password) { // 新用户直接使用ASP.NET默认哈希算法 return _defaultHasher.HashPassword(user, password); } public PasswordVerificationResult VerifyHashedPassword(ApplicationUser user, string hashedPassword, string providedPassword) { // 老Firebase用户走SCRYPT验证 if (!user.IsMigrated) { bool isValid = FirebaseScryptValidator.VerifyPassword( providedPassword, user.FirebaseSalt, hashedPassword, _configuration["Firebase:SignerKey"] ); if (isValid) { // 验证通过后自动将密码重存为ASP.NET默认哈希,标记为已迁移 user.IsMigrated = true; return PasswordVerificationResult.SuccessRehashNeeded; } return PasswordVerificationResult.Failed; } // 已迁移用户走默认验证逻辑 return _defaultHasher.VerifyHashedPassword(user, hashedPassword, providedPassword); } }
- 在Program.cs中替换默认密码哈希器:
builder.Services.AddDefaultIdentity<ApplicationUser>(options => { /* 你的Identity配置 */ }) .AddPasswordHasher<FirebaseCompatiblePasswordHasher>();
4. 常见踩坑点
- Firebase导出的
passwordHash和salt均为URL安全Base64编码,解码前必须替换特殊字符、补全填充位,否则哈希结果会完全不匹配 - 内存成本参数需按2的幂次计算,不要直接传入
mem_cost原始值 - 盐拼接顺序不要搞反,必须是用户salt在前,全局盐分隔符在后
内容的提问来源于stack exchange,提问作者Arkuni
相关产品推荐
相关产品推荐

