Search code examples
c#asp.netalgorithmhashmembership-provider

What Hash Algorithm used by the Asp.net Membership?


I'm trying to implement a custom store for passwords. Before changing a password, I need to check first whether the password has been used last 8 times the user changed the password.

var hashingAlgorithm = ConfigurationManager.AppSettings("MembershipProviderHashAlgorithm");
var hashedPasswordDetails = pwdHistory.GetRecentPasswordDetails(userName);
foreach (var passwordDetails_loopVariable in hashedPasswordDetails) 
{
  passwordDetails = passwordDetails_loopVariable;
  var encodedPassword = pwdEncr
            .EncodePassword(proposedNewPassword, passwordDetails.Salt, hashingAlgorithm);
  var hashedPassword = passwordDetails.HashedPassword;
  if (hashedPassword.Equals(encodedPassword)) //This line always return FALSE.
  {
     return true;               
  }
}
return false;

The problem I'm having is that the passwords returned from the tables are always different from what I type (event when in clear there are the same). That's because of the Hashing Algorithm.

I've tried SHA and SHA1 with no luck. Is there a particular hashing algorithm that ASP.NEt membership uses? I'm using System.Web.Security.SqlMembershipProvider version 4.0.0.0

<add key="MembershipProviderHashAlgorithm" value="SHA" />

Thanks for helping.

EDIT

Here's part of configuration from the membership section in the web.config. Is there a way to tell which algorithm's being used.

<membership defaultProvider="SqlProvider" userIsOnlineTimeWindow="15">
  <providers>
    <clear />
    <add name="SqlProvider" type="System.Web.Security.SqlMembershipProvider,                  
         System.Web, Version=4.0.0.0, Culture=neutral,                  
         PublicKeyToken=b03f5f7f11d50a3a"
         connectionStringName="myConnectionString"
         ../..
         requiresUniqueEmail="false"
         passwordFormat="Hashed"
         maxInvalidPasswordAttempts="5"
         minRequiredPasswordLength="7"
         passwordAttemptWindow="10"/>
  </providers>
</membership>

Solution

  • Membership Provider uses the following Algorithms to hash password.

    Default hash will vary based on the version of Membership Provider.

    ASP.Net Universal Provider

    MVC 4 and ASP.NET 4 and 4.5 default hash is SHA256 (HMACSHA256).

    public string EncodePassword(string pass, 
        MembershipPasswordFormat passwordFormat, string salt)
    {
        byte[] numArray;
        byte[] numArray1;
        string base64String;
    
        if (passwordFormat == MembershipPasswordFormat.Hashed)
        {
            byte[] bytes = Encoding.Unicode.GetBytes(pass);
            byte[] numArray2 = Convert.FromBase64String(salt);
            byte[] numArray3;
    
            // Hash password
            HashAlgorithm hashAlgorithm = HashAlgorithm.Create(Membership.HashAlgorithmType);
    
            if (hashAlgorithm as KeyedHashAlgorithm == null)
            {
                numArray1 = new byte[numArray2.Length + bytes.Length];
                Buffer.BlockCopy(numArray2, 0, numArray1, 0, numArray2.Length);
                Buffer.BlockCopy(bytes, 0, numArray1, numArray2.Length, bytes.Length);
                numArray3 = hashAlgorithm.ComputeHash(numArray1);
            }
            else
            {
                KeyedHashAlgorithm keyedHashAlgorithm = (KeyedHashAlgorithm)hashAlgorithm;
    
                if (keyedHashAlgorithm.Key.Length != numArray2.Length)
                {
    
                    if (keyedHashAlgorithm.Key.Length >= numArray2.Length)
                    {
                        numArray = new byte[keyedHashAlgorithm.Key.Length];
                        int num = 0;
                        while (true)
                        {
                            if (!(num < numArray.Length))
                            {
                                break;
                            }
                            int num1 = Math.Min(numArray2.Length, numArray.Length - num);
                            Buffer.BlockCopy(numArray2, 0, numArray, num, num1);
                            num = num + num1;
                        }
                        keyedHashAlgorithm.Key = numArray;
                    }
                    else
                    {
                        numArray = new byte[keyedHashAlgorithm.Key.Length];
                        Buffer.BlockCopy(numArray2, 0, numArray, 0, numArray.Length);
                        keyedHashAlgorithm.Key = numArray;
                    }
                }
                else
                {
                    keyedHashAlgorithm.Key = numArray2;
                }
                numArray3 = keyedHashAlgorithm.ComputeHash(bytes);
            }
    
            base64String = Convert.ToBase64String(numArray3);
        }
        else if (passwordFormat == MembershipPasswordFormat.Encrypted)
        {
            throw new NotImplementedException("Encrypted password method is not supported.");
        }
        else
        {
            base64String = pass;
        }
    
        return base64String;
    }
    

    Old ASP.Net Membership Provider

    Default hash algorithm is SHA-1.

    private string EncodePassword(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 = GetHashAlgorithm();
            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 { 
            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 = EncryptPassword(bAll, _LegacyPasswordCompatibilityMode); 
        }
    
        return Convert.ToBase64String(bRet); 
    }