Effective Password Encryption

2019-01-30 07:28发布

I've taken a look at the StackOverflow question, "Password Encryption / Database Layer AES or App Layer AES," and I'd like to effectively and efficiently hash my passwords on registration (web app) and then be able to check they are correct on login. I'm using VB, but comfortable using C#.

I would love to use Jeff Atwood's Encryption class described in ".NET Encryption Simplified" as it's really easy to understand. It has a hashing class—but I have no idea how to "login" and compare hashes after they have been hashed. This is Jeff's demonstration of his hash methods using his Encryption class:

Sub DemoHash()
    Dim d As New Encryption.Data( _
        "{ts '2004-10-09 08:10:04'}The world is beautiful and needs caring by its children")

    Dim hash As New Encryption.Hash(Encryption.Hash.Provider.SHA1)
    Dim hash2 As New Encryption.Hash(Encryption.Hash.Provider.SHA256)
    Dim hash3 As New Encryption.Hash(Encryption.Hash.Provider.SHA384)
    Dim hash4 As New Encryption.Hash(Encryption.Hash.Provider.SHA512)
    Dim hash5 As New Encryption.Hash(Encryption.Hash.Provider.MD5)
    Dim hash6 As New Encryption.Hash(Encryption.Hash.Provider.CRC32)

    hash.Calculate(d)
    hash2.Calculate(d)
    hash3.Calculate(d)
    hash4.Calculate(d)
    hash5.Calculate(d)

    Console.WriteLine("SHA1:   " & hash.Value.Hex)
    Console.WriteLine("SHA256: " & hash2.Value.Hex)
    Console.WriteLine("SHA384: " & hash3.Value.Hex)
    Console.WriteLine("SHA512: " & hash4.Value.Hex)
    Console.WriteLine("MD5:    " & hash5.Value.Hex)
    Console.WriteLine("CRC32:  " & hash6.Calculate(d).Hex)
    Console.WriteLine()

    Dim salt As New Encryption.Data("salty!")
    Console.WriteLine("Salted CRC32:  " & hash6.Calculate(d, salt).Hex)

    Console.WriteLine("Press ENTER to continue...")
    Console.ReadLine()
End Sub

So my questions are:

  1. I can encrypt the password (though I have no intention of storing it) and hash a string. If I were to have a user called 'barry' with a password of 'fishlegs', what is the best way to store his password and retrieve it?

  2. In SQL Server; is binary or nvarchar the best option for the storage of the hash?

  3. Based on 'barry' and his password what effectively is the hash storing? Is it an encryption of 'fishlegs' appended to a salt?

Cryptography is hard!

Thanks to anyone who can assist...

8条回答
我只想做你的唯一
2楼-- · 2019-01-30 07:54

OK so a couple of things about Jeff's class first of all. SHA1 and MD5 are deprecated now. CRC32 is not suitable at all for passwords. Secondly you should be salting every hash, preferably with a different salt value. Generally you choose a cryptographically random block of data for this, but at a push you could use the user name. To salt you prefix, or suffix the salt value somewhere in the process. I tend to hash the password, hash the salt, combine the two, then hash again. But you can swap things around it doesn't really matter that much, as long as you are consistent.

So rather than confuse things further with Jeff's class lets do this the classic way.

First off random salt generation.

public static byte[] GetRandomSalt()
{
  int minSaltSize = 16;
  int maxSaltSize = 32;

  Random random = new Random();
  int saltSize = random.Next(minSaltSize, maxSaltSize);
  saltBytes = new byte[saltSize];
  RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider();
  rng.GetNonZeroBytes(saltBytes); 
  return saltBytes;
}

Then hashing

public static byte[] ComputeHash(string plainText)
{
  byte[] plainTextBytes = Encoding.UTF8.GetBytes(plainText);
  HashAlgorithm hash = new SHA256Managed();
  return hash.ComputeHash(plainTextWithSaltBytes);
}

So this will compute a SHA256 hash and return it as a byte array.

If you were salting you'd do something like the following

byte[] passwordHash = ComputeHash(password);
byte[] salt = GetRandomSalt();
byte[] saltHash = ComputeHash(salt);

byte[] hashWithSaltBytes = new byte[hashBytes.Length + saltBytes.Length];
for (int i=0; i < hashBytes.Length; i++)
  hashWithSaltBytes[i] = hashBytes[i];
for (int i=0; i < saltBytes.Length; i++)
  hashWithSaltBytes[hashBytes.Length + i] = saltBytes[i];

And then, if you're bored hash it down again, or leave as is.

To turn a byte array into a string, if you don't fancy storing bytes you can use

string hashValue = Convert.ToBase64String(hashWithSaltBytes);

String comparisons are easier than byte comparisons where you have to iterate over each array, up to you. Just remember if you are using random salts you need to store them beside the password.

查看更多
成全新的幸福
3楼-- · 2019-01-30 07:58

ASP.NET includes a SQL based membership provider that allows you to create, store and verify encryped or hashed passwords, without you having to do anything - as Eric Lippert says:

let me give you all my standard caution about rolling your own cryptographic algorithms and security systems: don't.

查看更多
登录 后发表回答