tags:

views:

1380

answers:

9

I am hashing password using the .NET System.Security.Cryptography class. It has a few algorithms for hashing, e.g. MD5, SHA1, SHA256, SHA384, SHA512

The resultant hashed value is a byte array. Should i convert it to a hex string for storing, or Convert.ToBase64String(), or to something else? (I am favoring Base64 as it is shorter than Hex).

Incidentally with so many hashing algo's to pick from, i randomly chose SHA384, but is there one that is "better" or suitable to the task?

Comments please.

Update after reading first eight comments:
Going by the answers and further reading i've done, it seems MD5,SHA1 are more or less equivalent (with SHA1 being slightly more secure). SHA256, 384, 512 provide even better security in increasing order.

Since i won't be needing fort-knox (this is for an internal corporate system with no urls, browsers, internets, intranets, or extranets in sight), i will bypass the "salting" business - i figured if someone can steal the passwords table, they may as well steal the actual data in other tables.

But i will keep the "salt" concept for future reference; not sure if the salt should be appended (at the end) or prepended (at the front) of the password before hashing, and would it make a difference? Also i was thinking of using the first few chars of the password itself as the salt, to avoid an extra field to store it, but i guess it's not long enough - and the salt should be long enough.

The consensus says base64 conversion is a reasonable choice for storage and comparison. It remains for me to figure out what's the max database column lenght i will need for hash storage, given a max password lenght of 15 chars. Perhaps Varchar(64)?

Thank you everyone for your contribution.

+1  A: 

You could also convert it to plain bytes. Thus a 128 bits MD5 hash value would result in a 128 bit / 8 bit/byte = 16 byte long string.

Gumbo
The idea is generally sound - bytes can easily be stored as bytes. However, this may make manual manipulation somewhat difficult, since it's not generally possible to see binary data in any reasonable format using an editor/IDE/tool.
Sander
I'm not sure why this answer was voted down. If the hashed password is stored in a database such as MySQL and SQL Server, *both* of them (and many others I'm willing to bet) support fixed length binary fields, and this would be the most efficient way of storing AND comparing the hashes. The question makes no specific mention that the result MUST be stored as a character string.
Jason Musgrove
@Sander -- what? That's what you have an IDE for in the first place!
Christoffer
Why would you want to manipulate a hash value? And even if so, you don’t have to show the hash value as binary data to the user. But for storing, the binary data will be the optimum.
Gumbo
How would you store this binary data? remember the hashed value is an array of bytes, e.g. {1, 2, 3, 4, ..., 48}, where each of the values 1,2,3 can be any number between 0-255. Somehow i'd need to convert it into a string for storage in one field only. And the shorter this string, the more efficient for comparison purposes.
joedotnot
Each element would represent a byte. I don’t know .NET but there’s probably a function to convert your integer values into characters. Maybe http://msdn.microsoft.com/library/system.byte.tostring.aspx does this.
Gumbo
+3  A: 

Base64 is indeed shorter than hex, with the slight downside that you need to be careful if it ever ends up in a URL (due to the symbols involved). It depends where you're using it, basically. Hex normally has the advantage that it's easier to "read" with the naked eye, but as this will be essentially meaningless data, that's not relevant for a hash. (You could always use an online base64 decoded if necessary.)

This is all assuming you want a string, of course. Strings are nice and easy to store, transmit etc compared with opaque byte arrays.

Jon Skeet
+4  A: 

Incidentally with so many hashing algo's to pick from, i randomly chose SHA384, but is there one that is "better" or suitable to the task?

In general I would avoid MD5 or SHA-0 or SHA-1. But SHA-384 should be sufficient for your needs at the moment. See this question for some discussion on algorithms.

Phill Sacre
+2  A: 

Interesting note I've seen before, if you have an array of bytes, and you base64 encode it, some of the resulting characters can cause problems in urls - however, if you base 64 encode it a second time, those offending characters tend to be removed, and the base64 string can be used in urls. You can compare using the double encoded string.

On hashing algorithms, sha384 is probably a sufficient algorithm, but ensure that you salt your hash!

Kazar
Here is a very informative article about "salting your hash". Read it before rolling your own password hashing/storage thing.http://www.matasano.com/log/958/enough-with-the-rainbow-tables-what-you-need-to-know-about-secure-password-schemes/
Pavel Repin
Why would i have to decode it twice? can't i just hash the supplied password, encode it twice, and compare this with the stored hash (which has been encoded twice) ?
joedotnot
Very true, sorry, just my usage of this method in the past required me to get the byte content back out - edited appropriately.
Kazar
hex(string) takes only slightly more (12.5%) space than base64(base64(string)), is less confusing, is not case-sensitive, and does not contain any characters which would need to be escaped in URLs.
Justice
A: 

For passwords, I believe that any of them serves perfectly. The SHA 512 will create a key higher, reducing the chances of a "collision", knowing that different passwords may have the same hash, but a possibility is very low.

Fujiy
+4  A: 

To answer the second part of your question, SHA384 is probably overkill. SHA1 would serve your purposes just fine but there is a theoretical exploit for it (relating to being able to create a tampered document that hashes to the same value, nothing you need to worry about).

And in case you're not doing it, make sure you salt your passwords before hashing them to protect against the more common hacker attacks (identifying similar passwords, rainbow tables, etc). Read this article for more information.

For the record, I like Base64 since .NET has built-in routines for converting to/from it.

Talljoe
Why is the SHA-1 exploit "nothing you need to worry about"? If someone can manufacture a password that hashes to the same thing as my password, that's the same as breaking my password. I would go any lower than SHA-256 for passwords.
Graeme Perrow
Correction: I *wouldn't* go any lower than SHA-256.
Graeme Perrow
As I understand it, the current _theoretical_ exploit is based off of having the existing plaintext; that is -- creating an evil version of the plaintext. Finding a plaintext that hashes to a given value is still very difficult. And if you already have the password, it doesn't do ou much good to create an evil version of the password.
Talljoe
A: 

The answer depends on the context and constrain you might have. Base64 will be more compact but a bit more computing expensive to generate compared to hex encoding.

You should add a salt to your hashed data. It is a random value put in front of the pasword you hash. Then store the salt value with the hashed value so that you can regenerate the hash value.

The role of this salt is to prevent building dictionnary mapping common passwords to hashed values. If someone can get a copy of your hashed password file, he could use this dictionary to find the clear text password. With a 16bit salt you have 65536 different hash values for the same clear text password. The dictionnary attack becomes less efficient. Better pick a 64bit salt, because it won't add much more effort to hash and verify passwords.

You should also add a magic byte sequence (constant byte sequence) with the hashed value. The attacker will have to know this magic byte sequence in order to be able to generate the appropriate hash values. So he won't go very far if the only thing it has is the hashed password file. He will need to get the magic word that will be most likely burried in your code somewhere.

Regarding the algorithms to use SHA1 is very good and SHA256 would be belt and straps. MD5 is much less expensive to compute and would satisfy most use case. Consider this if your processing and storage resource might be a limiting factor.

chmike
why do you say put the salt in front of the password? why not at the back? would it make a difference?
joedotnot
Yes, it would make a slight difference. A hash function processes input bytes per bytes and it is possible to save and restore an intermediate state. If the salt is appended, you don't have to reahash the whole input string to get the hash. You simply start from the intermediate state. When the salt is put in front, the whole input string has to be rehashed which requires much more computation effort for brute force guessing.
chmike
A: 

I would use Base64 .... oh and dont forget to salt your hash if its a password you are hashing :) And anything smaller than SHA384 is a security risk if we talk about password, because is so easy to get quality rainbow tables for SHA1 and other common hashing algos.

psicho
I'd argue that anything less than SHA-256 is a security risk.
Graeme Perrow
+4  A: 

Even if your solution is not fort-knox, you should be diligent and implement salting. Because many people reuse their passwords elsewhere, and the break-in will do additional damage outside your organization, should the attackers choose to use the cracked password database for other purposes.

Salting makes dictionary attacks more expensive. By deciding what salt size to use you can fine tune your chances. Here's the quote from Bruce Schneier's "Applied Cryptography":

"Salt isn't a panacea; increasing the number of salt bits won't solve everything. Salt only protects against general dictionary attacks on a password file, not a concerted attack on a single password. It protects people who have the same password on multiple machines, but doesn't make poorly chosen passwords any better."

Here's a sample in C#. It ain't that hard. And you can choose what salt size and hash function to use. Disclaimer: use something like bcrypt if you really care about password integrity.


using System;
using System.IO;
using System.Reflection;
using System.Security.Cryptography;
using System.Text;

public class PassHash {
    private static readonly RandomNumberGenerator rng = RandomNumberGenerator.Create();
    public static readonly int DefaultSaltSize = 8; // 64-bit salt
    public readonly byte[] Salt;
    public readonly byte[] Passhash;

    internal PassHash(byte[] salt, byte[] passhash) {
        Salt = salt;
        Passhash = passhash;
    }

    public override String ToString() {
        return String.Format("{{'salt': '{0}', 'passhash': '{1}'}}",
                             Convert.ToBase64String(Salt),
                             Convert.ToBase64String(Passhash));
    }

    public static PassHash Encode<HA>(String password) where HA : HashAlgorithm {
        return Encode<HA>(password, DefaultSaltSize);
    }

    public static PassHash Encode<HA>(String password, int saltSize) where HA : HashAlgorithm {
        return Encode<HA>(password, GenerateSalt(saltSize));
    }

    private static PassHash Encode<HA>(string password, byte[] salt) where HA : HashAlgorithm {
        BindingFlags publicStatic = BindingFlags.Public | BindingFlags.Static;
        MethodInfo hasher_factory = typeof (HA).GetMethod("Create", publicStatic, Type.DefaultBinder, Type.EmptyTypes, null);
        using (HashAlgorithm hasher = (HashAlgorithm) hasher_factory.Invoke(null, null))
        {
            using (MemoryStream hashInput = new MemoryStream())
            {
                hashInput.Write(salt, 0, salt.Length);
                byte[] passwordBytes = Encoding.UTF8.GetBytes(password);
                hashInput.Write(passwordBytes, 0, passwordBytes.Length);
                hashInput.Seek(0, SeekOrigin.Begin);
                byte[] passhash = hasher.ComputeHash(hashInput);
                return new PassHash(salt, passhash);
            }
        }
    }

    private static byte[] GenerateSalt(int saltSize) {
        // This generates salt.
        // Rephrasing Schneier:
        // "salt" is a random string of bytes that is
        // combined with password bytes before being
        // operated by the one-way function.
        byte[] salt = new byte[saltSize];
        rng.GetBytes(salt);
        return salt;
    }

    public static bool Verify<HA>(string password, byte[] salt, byte[] passhash) where HA : HashAlgorithm {
        // OMG: I don't know how to compare byte arrays in C#.
        return Encode<HA>(password, salt).ToString() == new PassHash(salt, passhash).ToString();
    }
}

Usage:

New user submits their credentials.

PassHash ph = PassHash.Encode<SHA384>(new_user_password);

Store ph.Salt & ph.Passhash somewhere ... Later, when user logs in again, you look up the user record that has salt & passhash, and then you do this:

PassHash.Verify<SHA384>(user_login_password, user_rec.salt, user_rec.passhash)

}

Pavel Repin
Code handed to me on a plate? i like that :-) But i had done most of it anyway.
joedotnot
To compare byte arrays you can do:ba1.SequenceEqual(ba2); // I love Linq
Talljoe
+1 Talljoe, I'm dinosaur. I was proficient in C# 2.0 and whatever .NET FCL was at, when I had to completely swap out that part of the brain to make room for Python coding. Since then good things happened C#, it seems (like LINQ).
Pavel Repin