Transfer from membership to identifier when passwordFormat = Encryption and decryption = AES

I recently started developing a new MVC application, and I needed to take an older existing asp.net membership database and convert it to a new identity system.

If you find yourself in a similar situation, then most likely you came across this useful post from Microsoft , which contains excellent recommendations and scenarios for converting a database into a new scheme, including passwords.

To handle password hashing / encryption differences between the two systems, they include the SqlPasswordHasher user hash password, which parses the password field (which was combined into Password | PasswordFormat | Salt) and tries to duplicate the logic found in SqlMembershipProvider. Compare the incoming password with the saved version.

However, as I (and another commentator in this post) noticed that this handy hash they provided does not handle encrypted passwords (despite the confusing jargon that they use in the post, which seems to indicate this). It seems like it should be, given that they transfer the password format to the database, but, oddly enough, the code does not use it, instead having

int passwordformat = 1;

which for hashed passwords. What I needed was a script that would process my script, namely encrypted passwords using the System.Web / MachineKey decryptionKey configuration element.

If you are also in this predicament and using the AES algorithm (as defined in the machineKey decryption property), my answer below should come to your aid.

+5
source share
1 answer

-, , SqlMembershipProvider . , byte [] , Unicode, , . . (MembershipAdapter) MachineKeySection, .

, MachineKeySection IV ( ), . IV - , machineKey IV, , , . ( ), MachineKeySection , . :

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

    public override PasswordVerificationResult VerifyHashedPassword(string hashedPassword, string providedPassword)
    {
        string[] passwordProperties = hashedPassword.Split('|');
        if (passwordProperties.Length != 3)
        {
            return base.VerifyHashedPassword(hashedPassword, providedPassword);
        }
        else
        {
            string passwordHash = passwordProperties[0];
            int passwordformat = int.Parse(passwordProperties[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);
            }
        }
        else //MembershipPasswordFormat.Encrypted, aka 2
        {               
            byte[] bEncrypt = new byte[bSalt.Length + bIn.Length];
            Buffer.BlockCopy(bSalt, 0, bEncrypt, 0, bSalt.Length);
            Buffer.BlockCopy(bIn, 0, bEncrypt, bSalt.Length, bIn.Length);

            // Distilled from MachineKeyConfigSection EncryptOrDecryptData function, assuming AES algo and paswordCompatMode=Framework20 (the default)
            using (var stream = new MemoryStream())
            {
                var aes = new AesCryptoServiceProvider();
                aes.Key = HexStringToByteArray(MachineKey.DecryptionKey);
                aes.GenerateIV();
                aes.IV = new byte[aes.IV.Length];
                using (var transform = aes.CreateEncryptor())
                {
                    using (var stream2 = new CryptoStream(stream, transform, CryptoStreamMode.Write))
                    {
                        stream2.Write(bEncrypt, 0, bEncrypt.Length);
                        stream2.FlushFinalBlock();
                        bRet = stream.ToArray();
                    }
                }
            }               
        }

        return Convert.ToBase64String(bRet);
    }

    public static byte[] HexStringToByteArray(String hex)
    {
        int NumberChars = hex.Length;
        byte[] bytes = new byte[NumberChars / 2];
        for (int i = 0; i < NumberChars; i += 2)
            bytes[i / 2] = Convert.ToByte(hex.Substring(i, 2), 16);
        return bytes;
    }

    private static MachineKeySection MachineKey
    {
        get
        {
            //Get encryption and decryption key information from the configuration.
            System.Configuration.Configuration cfg = WebConfigurationManager.OpenWebConfiguration(System.Web.Hosting.HostingEnvironment.ApplicationVirtualPath);
            return cfg.GetSection("system.web/machineKey") as MachineKeySection;
        }
    }


}

, , MachineKeySection , . !

+6

All Articles