6

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>
2
  • 3
    If you haven't already, you can look at the source of SqlMembershipProvider Commented Apr 29, 2016 at 19:19
  • 2
    @TimMedora - referencesource is definitely the first place I look for code related issues in MSFT libraries also. It is a must. Commented Apr 29, 2016 at 19:20

1 Answer 1

8

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); 
}
Sign up to request clarification or add additional context in comments.

10 Comments

What you are suggesting is to replace the method used in this line var encodedPassword = pwdEncr .EncodePassword(proposedNewPassword, passwordDetails.Salt, hashingAlgorithm); with the above method? Is there a way to call an existing class/method that contains the above code?
Answer is yes. EncodePassword is a private method, so you cannot call it from outside.
The above method didn't help either. With the same clear password and the same salt, the above method is producing a hashed that is different from what membership is providing. I'm missing something?
Each membership's hashing algorithm is different. About code is from latest Membership Provider called ASP.NET Universal Providers. FYI: all Membership Providers has been replaced by ASP.Net Identity.
I used Resharper. Fortunately, the generated code is same as SqlMembershipProvider
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.