c# - 从 SHA1 迁移到 SHA2 ASP.net 4.5,C#

标签 c# asp.net .net sha

我们有一个在 .NET Framework 4.5 版本中构建的 ASP.NET Web 应用程序。目前在生产中,此应用程序使用 SHA1 加密算法。该算法在应用程序的 web.config 文件的“MachineKey”标签中设置。此应用程序使用 ASP.Net 成员身份概念来维护登录凭据。

由于 SHA1 算法即将退化,因此我们希望将我们的应用程序从 SHA1 更新到 SHA2。为此,我们在应用程序的 web.config 文件的“MachineKey”标签中设置了“HMACSHA256”。

使用上述设置将我们的应用程序升级到 SHA2 后,我们预计旧用户的密码(使用 SHA1 加密并且已经存在于成员数据库中)将无法使用 SHA2 算法。但它允许老用户在不修改之前加密的密码的情况下登录。

问题 1:在应用程序的 web.config 文件的“MachineKey”标签中所做的更改是否足以/推荐用于此迁移?

问题 2:由于我们仍然可以使用之前加密的密码登录应用程序,成员(member)数据库是否真的使用 web.config 文件中设置的 SHA2 加密?或者我们需要添加一些额外的设置以在成员数据库级别启用 SHA2 加密?请指教。

请建议是否有在成员(member)数据库级别启用 SHA2 加密的最佳方法。

最佳答案

我不知道是否可以在不强制用户完成密码重置过程的情况下通过成员资格处理此类迁移。

但是您可以通过同时将 Membership 迁移到 Asp.Net Identity 来做到这一点:Asp.Net Identity 具有扩展点,允许您处理“后备”密码签名匹配以支持旧签名。那时,您的内存中仍有未经哈希处理的登录名和密码,然后您可以顺便将签名转换为新格式。

所有这些都在以下blog中用详细信息和代码进行了解释,包括 SQL 迁移,我只是在下面代码的添加注释中稍微解释一下。

这是实现这个的主要类:

public class BackCompatPasswordHasher : PasswordHasher
{
    public override string HashPassword(string password)
    {
        return base.HashPassword(password);
    }

    public override PasswordVerificationResult VerifyHashedPassword(
        string hashedPassword, string providedPassword)
    {
        // Relies on SQL migration having formatted old hashes as
        // (aspnet_Membership.Password + '|' + 
        // CAST(aspnet_Membership.PasswordFormat as varchar) + '|'
        // + aspnet_Membership.PasswordSalt)
        string[] passwordProperties = hashedPassword.Split('|');
        if (passwordProperties.Length != 3)
        {
            return base.VerifyHashedPassword(hashedPassword, 
                providedPassword);
        }
        else
        {
            string passwordHash = passwordProperties[0];
            int passwordformat = 1;
            string salt = passwordProperties[2];
            if (String.Equals(EncryptPassword(providedPassword,
                passwordformat, salt), 
                passwordHash, StringComparison.CurrentCultureIgnoreCase))
            {
                return PasswordVerificationResult.SuccessRehashNeeded;
            }
            else
            {
                return PasswordVerificationResult.Failed;
            }
        }
    }

    //This is copied from the existing SQL providers and is provided only 
    // for back-compat.
    private string EncryptPassword(string pass, int passwordFormat, 
         string salt)
    {
        if (passwordFormat == 0) // MembershipPasswordFormat.Clear
            return pass;

        byte[] bIn = Encoding.Unicode.GetBytes(pass);
        byte[] bSalt = Convert.FromBase64String(salt);
        byte[] bRet = null;

        if (passwordFormat == 1)
        { // MembershipPasswordFormat.Hashed 
            HashAlgorithm hm = HashAlgorithm.Create("SHA1");
            if (hm is KeyedHashAlgorithm)
            {
                KeyedHashAlgorithm kha = (KeyedHashAlgorithm)hm;
                if (kha.Key.Length == bSalt.Length)
                {
                    kha.Key = bSalt;
                }
                else if (kha.Key.Length < bSalt.Length)
                {
                    byte[] bKey = new byte[kha.Key.Length];
                    Buffer.BlockCopy(bSalt, 0, bKey, 0, bKey.Length);
                    kha.Key = bKey;
                }
                else
                {
                    byte[] bKey = new byte[kha.Key.Length];
                    for (int iter = 0; iter < bKey.Length; )
                    {
                        int len = Math.Min(bSalt.Length, bKey.Length - iter);
                        Buffer.BlockCopy(bSalt, 0, bKey, iter, len);
                        iter += len;
                    }
                    kha.Key = bKey;
                }
                bRet = kha.ComputeHash(bIn);
            }
            else
            {
                byte[] bAll = new byte[bSalt.Length + bIn.Length];
                Buffer.BlockCopy(bSalt, 0, bAll, 0, bSalt.Length);
                Buffer.BlockCopy(bIn, 0, bAll, bSalt.Length, bIn.Length);
                bRet = hm.ComputeHash(bAll);
            }
        }

        return Convert.ToBase64String(bRet);
    }
}

然后,在您的用户管理器中:

public class IdentityUserManager : UserManager<IdentityUser>
{
    public IdentityUserManager(IUserStore<IdentityUser> store)
        : base(store)
    {
        PasswordHasher = new BackCompatPasswordHasher();
    }
}

在我的实际代码库中,我添加了一些用于处理重新散列的内容,但不幸的是,没有评论为什么。也许那是原始实现者的一些多余代码,或者确实需要它。我还没有调查它,所以这是 IdentityUserManager 中的附加代码:

    private ConcurrentDictionary<string, string> UserRehashed = 
        new ConcurrentDictionary<string, string>();

    private bool CanRehash(IdentityUser user)
    {
        return UserRehashed.TryAdd(user.Id, user.Id);
    }

    protected async override Task<bool> VerifyPasswordAsync(
        IUserPasswordStore<IdentityUser, string> store, IdentityUser user,
        string password)
    {
        var hash = await store.GetPasswordHashAsync(user).ConfigureAwait(false);
        var verifPassRes = PasswordHasher.VerifyHashedPassword(hash, password);
        if (verifPassRes == PasswordVerificationResult.SuccessRehashNeeded &&
            // avoid rehash loop.
            CanRehash(user))
        {
            var chPassRes = await this.ChangePasswordAsync(user.Id,
                password, password).ConfigureAwait(false);
            if (!chPassRes.Succeeded)
            {
                // throw or log, whatever.
            }
        }

        return verifPassRes != PasswordVerificationResult.Failed;
    }

关于c# - 从 SHA1 迁移到 SHA2 ASP.net 4.5,C#,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34928787/

相关文章:

c# - 如何告诉 ASP.NET 不要在请求中插入控件的值?

.net - 如何在 GridView 模式下向 ListViewItem、ListView 添加边框

c# - Nullable<long> 在从 long 赋值后不是 Nullable<long>

javascript - 无法获取属性 'addEventListener'

c# - 无法加载资源: the server responded with a status of 500 (Internal Server Error) Azure MVC

asp.net - 在asp.net中获取Facebook Page Feed(使用graph api),收到错误 "Unsupported Browser"

asp.net - IIS Url Rewrite RegEx 与 .NET RegEx 的差异

.net - 类似于 VS 2010 Ultimate 的依赖关系图?

c# - 在 Asp Net Core 6 中使用 Github 进行身份验证

c# - 如何序列化 Windows.Media.Brush