Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ASP.NET membership salt?

How does ASP.NET membership generate their salt key and then how do they encode it (that is, is it salt + password or password + salt)?

I am using SHA-1 with my membership, but I would like to recreate the same salts so the built-in membership stuff could hash the stuff the same way as my stuff can.

Edit 2: Never mind. I misread it and was thinking it said bytes, not bits. So I was passing in 128 bytes, not 128 bits.

Edit: I been trying to make it so. This is what I have,

public string EncodePassword(string password, string salt)
{
    byte[] bytes = Encoding.Unicode.GetBytes(password);
    byte[] src = Encoding.Unicode.GetBytes(salt);
    byte[] dst = new byte[src.Length + bytes.Length];

    Buffer.BlockCopy(src, 0, dst, 0, src.Length);
    Buffer.BlockCopy(bytes, 0, dst, src.Length, bytes.Length);

    HashAlgorithm algorithm = HashAlgorithm.Create("SHA1");

    byte[] inArray = algorithm.ComputeHash(dst);

    return Convert.ToBase64String(inArray);
}

private byte[] createSalt(byte[] saltSize)
{
    byte[] saltBytes = saltSize;

    RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider();
    rng.GetNonZeroBytes(saltBytes);

    return saltBytes;
}

So I have not tried to see if the ASP.NET membership will recognize this yet the hashed password looks close. I just don't know how to convert it to base64 for the salt.

I did this

byte[] storeSalt = createSalt(new byte[128]);
string salt = Encoding.Unicode.GetString(storeSalt);
string base64Salt = Convert.ToBase64String(storeSalt);

int test = base64Salt.Length;

Test length is 172 which is well over the 128 bits so what am I doing wrong?

This is what their salt looks like

vkNj4EvbEPbk1HHW+K8y/A==

This is what my salt looks like

E9oEtqo0livLke9+csUkf2AOLzFsOvhkB/NocSQm33aySyNOphplx9yH2bgsHoEeR/aw/pMe4SkeDvNVfnemoB4PDNRUB9drFhzXOW5jypF9NQmBZaJDvJ+uK3mPXsWkEcxANn9mdRzYCEYCaVhgAZ5oQRnnT721mbFKpfc4kpI=
like image 311
chobo2 Avatar asked Aug 12 '09 18:08

chobo2


3 Answers

What is default hash algorithm that ASP.NET membership uses? has a good discussion of their default algorithm.

I hope that helps!

Edit- The answer I was referring to is the code in the top post,

   public string EncodePassword(string pass, string salt)
    {
        byte[] bytes = Encoding.Unicode.GetBytes(pass);
        //byte[] src = Encoding.Unicode.GetBytes(salt); Corrected 5/15/2013
        byte[] src = Convert.FromBase64String(salt); 
        byte[] dst = new byte[src.Length + bytes.Length];
        Buffer.BlockCopy(src, 0, dst, 0, src.Length);
        Buffer.BlockCopy(bytes, 0, dst, src.Length, bytes.Length);
        HashAlgorithm algorithm = HashAlgorithm.Create("SHA1");
        byte[] inArray = algorithm.ComputeHash(dst);
        return Convert.ToBase64String(inArray);
    } 

They are combining Unicode Salt + Pass using BlockCopy

-- In response to your question:

Both algorithms are necessary and fulfill different roles...

RNG Crypto is used to generate the salt. It is basically a long string of random data. This is generated and stored on a per user basis. Typically this is done when a user is created or a password is changed.

BlockCopy is just the method they use to combine the salt with the password. The above code essentially equates to salt + password.

You aren't going to be able to recreate a salt value as it is completely random. It is, however, stored for each user by the framework.

Combining the salt with the password and hashing it using the technique above will allow you to verify users passwords using the hashed value stored by the framework.

I think we both read your question differently. The code I posted won't generate your salt, but it will let you use it in a way that is compatible with ASP.NET membership.

Sorry my explanation isn't the best- does that answer your question?

like image 134
Kelly Robins Avatar answered Nov 05 '22 03:11

Kelly Robins


Here is how the SQLMembershipProvider generates salt.

    private string GenerateSalt() {
        var buf = new byte[16];
        (new RNGCryptoServiceProvider()).GetBytes(buf);
        return Convert.ToBase64String(buf);
   }

You can download the ASP.NET's SQL Provider code here.

The issue I was having was a development application running on IIS7. .NET 4.0 was using a different default hash algorithm than the default HashAlgorithmType for .NET 2.0.

In the "EncodePassword" sample code by Microsoft, they reference Membership.HashAlgorithmType which I believe returns the default for the framework, if it is not specified in the web.config.

I was able to get both this GenerateSalt and EncodePassword method to work for my application.

My combined code:

internal string GenerateSalt()
{
    byte[] buf = new byte[16];
    (new RNGCryptoServiceProvider()).GetBytes(buf);
    return Convert.ToBase64String(buf);
}

internal 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[] bAll = new byte[bSalt.Length + bIn.Length];
    byte[] bRet = null;

    Buffer.BlockCopy(bSalt, 0, bAll, 0, bSalt.Length);
    Buffer.BlockCopy(bIn, 0, bAll, bSalt.Length, bIn.Length);
    if (passwordFormat == 1)
    { // MembershipPasswordFormat.Hashed
        HashAlgorithm s = HashAlgorithm.Create("SHA1");
        // Hardcoded "SHA1" instead of Membership.HashAlgorithmType
        bRet = s.ComputeHash(bAll);
    }
    else
    {
        bRet = EncryptPassword(bAll);
    }
    return Convert.ToBase64String(bRet);
}
like image 45
RhinoDevX64 Avatar answered Nov 05 '22 03:11

RhinoDevX64


Here is one way of doing it. A salt is just a random number, you can use RNGCryptoServiceProvider class in the framework library to produce good random number to use as salt

private const int ITERATIONS = 10000;
private const int SALT_SIZE = 32;
private const int HASH_SIZE = 32;

public void SaltAndHashPassword(string password, out byte[] salt,
  out byte[] hash)
{
  Rfc2898DeriveBytes rdb = new Rfc2898DeriveBytes(
    password,
    SALT_SIZE,
    ITERATIONS);

  salt = rdb.Salt;
  hash = rdb.GetBytes(HASH_SIZE);
}
like image 1
Srikar Doddi Avatar answered Nov 05 '22 05:11

Srikar Doddi