2

I've developed a website that uses ASP.NET membership. Based on comments from previous sites, I decided to encrypt passwords so they could be recovered for users who forgot them.

However, the new site (which now has over 500 registered users) has brought me some criticism that the industry standard is really to hash passwords.

However, after a fairly extensive search, I have been unable to find anything about how to convert existing users' passwords from encrypted to hashed.

I know I can change the web.config file, and new users' passwords will use the new format. But it does nothing to update the existing users.

Note: I previously asked a similar question but mostly just got a debate about which is better, encrypted or hashed. I'm past that discussion but I've been unable to find a way to convert them without losing the hundreds of users already registered.

Jonathan Wood
  • 65,341
  • 71
  • 269
  • 466

3 Answers3

3

it seems you already know how to decrypt the passwords and change the web.config file, but you're stuck with how to implement the rest of the process.

using ILSpy, here's how to generate the salt for each user:

byte[] array = new byte[16];
new RNGCryptoServiceProvider().GetBytes(array);
return Convert.ToBase64String(array);    

once you have the salt, here's how to generate the password:

byte[] bytes = Encoding.Unicode.GetBytes(pass);
byte[] array = Convert.FromBase64String(salt);
byte[] array2 = new byte[array.Length + bytes.Length];
Buffer.BlockCopy(array, 0, array2, 0, array.Length);
Buffer.BlockCopy(bytes, 0, array2, array.Length, bytes.Length);   
using (SHA1CryptoServiceProvider sha1 = new SHA1CryptoServiceProvider()) {
  return Convert.ToBase64String(sha1.ComputeHash(array2));
}

where pass is the plain-text password you calculated, and salt is the string calculated in the first code snippet above. the default algorithm is SHA1, if you're wondering why it's being used.

since this is a one-time process, i would write a HTTP handler to manually update the database during a short, scheduled maintenance period - hopefully you have that luxury. (obviously make a backup and test first). you need to update the following fields in the aspnet_Membership table:

  1. Password - calculated above
  2. PasswordFormat - 1
  3. PasswordSalt - calculated above

never had to do anything like this, but hopefully that will get you started :)

kuujinbo
  • 9,272
  • 3
  • 44
  • 57
  • Cool. I can't get to this right away but looks like a good start. (BTW, that's great to hear about ILSpy. We need this now that Red Gate has acquired .NET Reflector!) – Jonathan Wood Apr 27 '11 at 14:33
  • actually Red Gate acquired .NET Reflector a long time ago and everything was OK - until they [decided to get nasty](http://kuujinbo.info/cs/dotnetReflectorIsDead.aspx). – kuujinbo Apr 27 '11 at 22:41
  • Yes, I know. But the product has been an increasing pain to use ever since. – Jonathan Wood Apr 27 '11 at 22:52
1

Maybe I'm missing something here, but it should be pretty simple. Create a process to decrypt the password, then salt accordingly and store the hash of the salt + user's decrypted password in the database. Obviously you don't want to be hashing the user's encrypted password. Don't forget to store the salt too.

kappasims
  • 124
  • 9
  • And just to clarify, you should be putting the salt at the end of the password, not before, when you hash it. ASP.NET Auth uses SHA1 by default I believe. – kappasims Apr 27 '11 at 02:41
  • I know conceptually what needs to be done. I guess there's enough about ASP.NET membership where I'm unclear on a number of the specifics. – Jonathan Wood Apr 27 '11 at 03:01
  • (I'm just typing gibberish for "encrypted" values). user "kappasims", password "test". Encrypted password: 83@#!s34. Decrypted password "test". Decrypt 83@#!s34 to get "test". Generate a random salt: "12345". Then concatenate into "test12345". Hash "test12345" into "!@83104234!@#$@#" using SHA1. Store "!@83104234!@#$@#" as the hash and "12345" as the salt. Lather. Rinse. Repeat. – kappasims Apr 27 '11 at 03:05
1

IMHO, Greg's response (and the associated comments) on your previous question (Changing passwordFormat from Encrypted to Hashed) is the way to go. Essentially, you want to:

  1. Add a hashed membership provider
  2. Loop through all of the encrypted password users,
  3. For each one decrypt the password, create the hash, store it, delete the encrypted version from the database, and move on.

When you are done, all of the encrypted password users should be converted to hashed.

Community
  • 1
  • 1
Chris Shain
  • 50,833
  • 6
  • 93
  • 125
  • Did you see my final comment to Greg? I basically stepped through his suggestion but, in the end, I was obviously missing something. Unfortunately, he didn't respond to that comment and it ended there. I guess there's enough I don't know about ASP.NET membership to where I wasn't able to make this happen. – Jonathan Wood Apr 27 '11 at 03:02
  • When you say "store it", what does that mean? Through the membership provider or directly? I'm not clear on how to do this without losing existing data. How do you juggle multiple providers and specify which does what? – Jonathan Wood Apr 27 '11 at 03:05
  • The idea is that you *want* to lose existing data (the encrypted passwords). As per the comments in the previous post, you'd want to use MembershipUser.GetPassword() to get the current password from the encrypted provider. This is possible because you are using reversible encryption- the method won't work with a hashed provider). Once you have the decrypted password, you want to hash it (with a salt). This is a one-way process. You then store the resulting hash of the password using the hashed membership provider (effectively overwriting the encrypted password with the hash). (continued) – Chris Shain Apr 27 '11 at 19:41
  • You then authenticate all users with the new hashed provider. A hashed provider authenticates users by taking the password that they provide, hashing it using the same algorithm that was used to hash the original (stored) password, and comparing the two hashes. If the password the user enters when logging in matches the one they used when setting up the account, then the resulting hashes will match. And if someone breaks into your database, they don't have your user's passwords. They just have the hashes, which (hopefully) won't do them much good. For more on hashing, see: http://bit.ly/dlRWpc – Chris Shain Apr 27 '11 at 19:45
  • Well I look at it as converting existing data rather than losing it. :-) I'm stuck on another project right now but will go over your comments in detail when I can. Thanks for the additional information. – Jonathan Wood Apr 27 '11 at 19:50