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

将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框架,可直接替换默认密码验证器,实现平滑迁移:

  1. 给用户表扩展两个字段:FirebaseSalt(存储老用户的原始salt)、IsMigrated(标记用户是否已完成哈希算法迁移)
  2. 实现自定义密码哈希器:
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);
    }
}
  1. 在Program.cs中替换默认密码哈希器:
builder.Services.AddDefaultIdentity<ApplicationUser>(options => { /* 你的Identity配置 */ })
    .AddPasswordHasher<FirebaseCompatiblePasswordHasher>();

4. 常见踩坑点

  • Firebase导出的passwordHash和salt均为URL安全Base64编码,解码前必须替换特殊字符、补全填充位,否则哈希结果会完全不匹配
  • 内存成本参数需按2的幂次计算,不要直接传入mem_cost原始值
  • 盐拼接顺序不要搞反,必须是用户salt在前,全局盐分隔符在后

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.01 04:36:01