Migrating from SHA1 to SHA2 ASP.net 4.5, C #

We have one ASP.NET web application, which is built in version .NET Framework 4.5. Currently, this application uses the SHA1 encryption algorithm in production. This algorithm is installed in the MachineKey tag of the web.config application. This application uses the ASP.Net membership concept to support login credentials.

Since the SHA1 algorithm is on the verge of degradation, so we want to upgrade our application from SHA1 to SHA2. To do this, we installed "HMACSHA256" in the "MachineKey" tag of the web.config application file.

After updating our application to SHA2 with the above settings, we expect that older user passwords (which were encrypted using SHA1 and are already present in the membership database) will not work with the SHA2 algorithm. But it allows older users to log in without any changes to a previously encrypted password.

Question 1: Are the changes made to the "MachineKey" tag of the web.config application sufficient / recommended for this migration?

Question 2: Since we can still enter the application using previously encrypted passwords, does the membership database really use the SHA2 encryption set in the web.config file? Or do we need to add additional settings to enable SHA2 encryption at the membership database level? Please advice.

Please suggest if there is a better way to enable SHA2 encryption at the membership database level.

+4
source share
3 answers

I don’t know whether it is possible to handle such a migration through membership without forcing users to go through the reset password process.

But you can do this by moving your Asp.Net Identity membership at the same time: Asp.Net Identity has extension points that allow you to process a “spare” password signature to support the old one. At this point, you still have a login password and a password that you can lose in memory, and then you can convert the signature to a new format.

, , 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;
    }
+2

, , "".

, , .

, . ( 1 2), db, PasswordFormat, .

, , SHA-2 . , .

, , .

+2

The whole point of upgrading from SHA1 to SHA2 is to actually reduce the known security issue SHA1, which was cracked many years ago. Therefore, trying and having a hybrid system is basically pointless.

Short-term: 99% of users are on SHA1 .. and only NEW users 1% of all users are on SHA2 ?? :(

You just need to put in place a password change that makes 100% of people on SHA2

0
source

Source: https://habr.com/ru/post/1625358/


All Articles