13

I've been reading this article from MSDN on Rfc2898DeriveBytes. Here is the sample encryption code they provide.

string pwd1 = passwordargs[0];
// Create a byte array to hold the random value. 
byte[] salt1 = new byte[8];
using (RNGCryptoServiceProvider rngCsp = ne RNGCryptoServiceProvider())
{
    // Fill the array with a random value.
    rngCsp.GetBytes(salt1);
}

//data1 can be a string or contents of a file.
string data1 = "Some test data";
//The default iteration count is 1000 so the two methods use the same iteration count.
int myIterations = 1000;
try
{
    Rfc2898DeriveBytes k1 = new Rfc2898DeriveBytes(pwd1,salt1,myIterations);
    Rfc2898DeriveBytes k2 = new Rfc2898DeriveBytes(pwd1, salt1);
    // Encrypt the data.
    TripleDES encAlg = TripleDES.Create();
    encAlg.Key = k1.GetBytes(16);
    MemoryStream encryptionStream = new MemoryStream();
    CryptoStream encrypt = newCryptoStream(encryptionStream, encAlg.CreateEncryptor(), CryptoStreamMode.Write);
    byte[] utfD1 = new System.Text.UTF8Encoding(false).GetBytes(data1);

    encrypt.Write(utfD1, 0, utfD1.Length);
    encrypt.FlushFinalBlock();
    encrypt.Close();
    byte[] edata1 = encryptionStream.ToArray();
    k1.Reset();

My question is, how would I properly Read/Write the hashed data to/from a text file?

My main goal is to do what this developer is doing. I need to store a password locally. When my application prompts the user for the password, the user will enter the password, then my application will read from the text file and verify if the password that the user entered is indeed correct. How would I go about doing it?

Community
  • 1
  • 1
Emma Geller-Green
  • 267
  • 1
  • 3
  • 9
  • 1
    it'd just be text. hash the user's pw, read the stored hash from file, compare the two hashes. if they're equal, then it's the correct pw. – Marc B Oct 26 '15 at 21:13
  • @MarcB - Can you provide an example of password hashing? – Emma Geller-Green Oct 26 '15 at 21:14
  • 5
    Why are you encrypting the password? Passwords are normally hashed, not encrypted. Hashing and encryption are not the same thing. – Tim Oct 26 '15 at 21:15
  • here is the msdn example- https://msdn.microsoft.com/en-us/library/aa545602%28v=cs.70%29.aspx notice they also use salts, which add security against dictionary attacks – mikibest2 Oct 26 '15 at 21:15
  • @Tim - You are indeed correct. – Emma Geller-Green Oct 26 '15 at 21:17
  • @MrRobot You can Simply hash the passwords using something like SHA512. But if you want to encrypt the password there is a win32 API called CryptProtectData which is used to store passwords in some applications like Internet Explorer. – Amir Oct 26 '15 at 21:17
  • 1
    @mikibest2: That code is wrong on many levels. Do not use it. Instead, use `Rfc2898DeriveBytes` – SLaks Oct 26 '15 at 21:17
  • 1
    @Amir: SHA512 is too fast for passwords. Use Rfc2898DeriveBytes – SLaks Oct 26 '15 at 21:18
  • @mikibest2 - Just for clarification, if the user enters a password, I would perform the proceedure mentioned in the article and then compare the two hash results(which is stored as strings), correct? – Emma Geller-Green Oct 26 '15 at 21:18
  • @SLaks really? what is wrong with it? I wouldv'e thought the msdn examples are solid :\ And Mr robot- yes, you do the exact same thing and compare with the stored value – mikibest2 Oct 26 '15 at 21:19
  • @SLaks - Forgive me, but what's is wrong with the code provided in the article? – Emma Geller-Green Oct 26 '15 at 21:19
  • @mikibest2: Unfortunately, they aren't. It uses insecure randomness, too-fast hashes, and dangerously confuses strings and bytes. – SLaks Oct 26 '15 at 21:20
  • @SLaks - Could you provide me with a way to properly write the data to a text file when using Rfc2898DeriveBytes? – Emma Geller-Green Oct 26 '15 at 21:21
  • @SLaks well I didn't look into the Random class, but I can agree you need special secure generators for encryption... are there any c# ones? and about the fast hash, any better c# ones? If this are the problems, one can substitue parts of the code to make it better, while keeping the structure – mikibest2 Oct 26 '15 at 21:21
  • @MrRobot: Check my link – gmoniava Oct 26 '15 at 21:24
  • 1
    @MrRobot - Keep in mind that, in any of the MSDN examples, they are provided to show you how to do something. That does NOT mean it is the way you should do it or that it is the best way to do something. MSDN is showing you an example only. – Icemanind Oct 26 '15 at 21:27
  • It seems you want to encrypt something with a password. You don't need to store the hash of the password. Use the typed in password to derive an encryption key and a second HMAC key. Use a million or more iterations for both and different salts. Store the salts in your file format along with the ciphertext, run HMAC over the ciphertext and store the produced hash. When the user is entering in the password again, then read the salts, derive the HMAC key and run HMAC again over the ciphertext to check if the password is correct. If it is then derive the decryption key. – Artjom B. Oct 26 '15 at 21:31

3 Answers3

5

You typically store the hash of the password, then when user enters password, you compute hash over the entered password and compare it with the hash which was stored - that said, just hashing is usually not enough (from security point of view) and you should use a function such as PKBDF2 (Password-Based Key Derivation Function 2) instead. Here is article covering all that information in more elaborate way as well as sample code (bottom of the page): http://www.codeproject.com/Articles/704865/Salted-Password-Hashing-Doing-it-Right

Here is a link to codereview, which I guess refers to the same implementation as above article.

Community
  • 1
  • 1
gmoniava
  • 18,228
  • 5
  • 33
  • 74
  • Thanks for the example. – Emma Geller-Green Oct 26 '15 at 21:24
  • Quick question, how and where would I store the password to be retrieved and hashed? I don't want to hard code into my application. – Emma Geller-Green Oct 26 '15 at 21:26
  • @MrRobot: You don't store the password you store the result of PKBDF2 which is safe to store – gmoniava Oct 26 '15 at 21:29
  • How this is asking alot but could you show me how to implement PKBDF2 and hashing? – Emma Geller-Green Oct 26 '15 at 21:29
  • @MrRobot: Did you check the link? all is there – gmoniava Oct 26 '15 at 21:30
  • Wait, see how in the code the CreateHash method, asks for a password which is a String, so when I call this method, I have to provide the password in plain text? – Emma Geller-Green Oct 26 '15 at 21:36
  • @MrRobot: Yes you want to create hash from a plain password. Check compare function also and its documentation – gmoniava Oct 26 '15 at 21:40
  • 2
    @MrRobot consider finding an open source implementation of PKBDF2. It is generally not a good idea to implement cryptographic functions yourself, better use well known and tested implementations. Other then that, great guide! – mikibest2 Oct 26 '15 at 21:40
  • @mikibest2: Seen that code also on codereview, so should be trustable – gmoniava Oct 26 '15 at 21:42
  • @MrRobot: It will stay in RAM but it is better then keeping it plain in file. See SecureString also. I doubt I can help further – gmoniava Oct 26 '15 at 21:46
  • 1
    @MrRobot You should really read some documentations first. Cryptography is really complex topic. Really great website to start with password hashing is [this one](https://crackstation.net/hashing-security.htm). I really recommend you to read at least the _The RIGHT Way: How to Hash Properly_ paragraph, – ProXicT Oct 26 '15 at 21:51
5

How to properly store password locally

Just don't do it. No really don't do it.

...But if you really really have to, never just implement it yourself. I would recommend reviewing how ASP.NET Identity hashes passwords. Version 3 is pretty rock solid at the moment:

note that the following is taken from github.com and may be changed at any time. For the latest, please refer to the previous link.

private static byte[] HashPasswordV3(string password, RandomNumberGenerator rng, KeyDerivationPrf prf, int iterCount, int saltSize, int numBytesRequested)
    {
        // Produce a version 3 (see comment above) text hash.
        byte[] salt = new byte[saltSize];
        rng.GetBytes(salt);
        byte[] subkey = KeyDerivation.Pbkdf2(password, salt, prf, iterCount, numBytesRequested);

        var outputBytes = new byte[13 + salt.Length + subkey.Length];
        outputBytes[0] = 0x01; // format marker
        WriteNetworkByteOrder(outputBytes, 1, (uint)prf);
        WriteNetworkByteOrder(outputBytes, 5, (uint)iterCount);
        WriteNetworkByteOrder(outputBytes, 9, (uint)saltSize);
        Buffer.BlockCopy(salt, 0, outputBytes, 13, salt.Length);
        Buffer.BlockCopy(subkey, 0, outputBytes, 13 + saltSize, subkey.Length);
        return outputBytes;
    }
Erik Philips
  • 48,663
  • 7
  • 112
  • 142
  • 1
    Thanks for this. My application is going to send e-mail from a specific computer out other employees, so I need to find a way to store the account password locally. Otherwise, as you stated above, I won't do it. – Emma Geller-Green Oct 26 '15 at 22:26
  • Why would that require you to store a password? – Erik Philips Oct 26 '15 at 22:26
  • I'm using an Hotmail account along with my application. When sending e-mail it asks for the user name/password for the e-mail account. I don't want to store the password in a plain text file. I needed a way to hash it, and properly read into the application. – Emma Geller-Green Oct 26 '15 at 22:28
  • Do you know another way I can approach this problem? – Emma Geller-Green Oct 26 '15 at 22:32
  • 1
    Hotmail should allow you to create an application password that is specific to your application to send as the password. This would not be the users password but [a separate application password to the account](http://windows.microsoft.com/en-us/windows/app-passwords-two-step-verification). I don't think you need two-step verification to turn this on, but it will be insanely more security then actually storing the password. – Erik Philips Oct 26 '15 at 22:32
  • Thanks for this, I have to look into how to set this up. – Emma Geller-Green Oct 26 '15 at 22:37
  • Could I use DPAPI? Long story short, I used to have this SO account but can't access it right now. http://chat.stackexchange.com/rooms/30269/discussion-between-sejpm-and-nexusfactor. It sums up what I what in my application. This one too: http://crypto.stackexchange.com/questions/29845/properly-storing-an-e-mail-password Take a look. – Emma Geller-Green Oct 26 '15 at 22:41
  • If I go with DPAPI, could you show me how to properly implement it? – Emma Geller-Green Oct 26 '15 at 22:44
3

You should store the password as a one-way hash and the salt used to create that password. This way you are absolutely sure that the password for the user can never be DECRYPTED. Never use any two-way encryption for this particular task, as you risk exposing user information to would-be attackers.

void Main()
{
    string phrase, salt, result;
    phrase = "test";
    result = Sha256Hash(phrase, out salt);

    Sha256Compare(phrase, result, salt);
}

public string Sha256Hash(string phrase, out string salt)
{
    salt = Create256BitSalt();
    string saltAndPwd = String.Concat(phrase, salt);
    Encoding encoder = Encoding.Default;
    SHA256Managed sha256hasher = new SHA256Managed();
    byte[] hashedDataBytes = sha256hasher.ComputeHash(encoder.GetBytes(saltAndPwd));
    string hashedPwd = Encoding.Default.GetString(hashedDataBytes);
    return hashedPwd;
}

public bool Sha256Compare(string phrase, string hash, string salt)
{
    string saltAndPwd = String.Concat(phrase, salt);
    Encoding encoder = Encoding.Default;
    SHA256Managed sha256hasher = new SHA256Managed();
    byte[] hashedDataBytes = sha256hasher.ComputeHash(encoder.GetBytes(saltAndPwd));
    string hashedPwd = Encoding.Default.GetString(hashedDataBytes);
    return string.Compare(hash, hashedPwd, false) == 0;
}

public string Create256BitSalt()
{
    int _saltSize = 32;
    byte[] ba = new byte[_saltSize];
    RNGCryptoServiceProvider.Create().GetBytes(ba);
    return Encoding.Default.GetString(ba);
}

You could also figure out another method for obtaining the salt, but I have made mine to that it computes 2048 bits worth of random data. You could just use a random long you generate but that would be a lot less secure. You won't be able to use SecureString because SecureString isn't Serializable. Which the whole point of DPAPI. There are ways to get the data out but you end up having to jump a few hurdles to do it.

FWIW, PBKDF2 (Password-Based Key Derivation Function 2) is basically the same thing as SHA256 except slower (a good thing). On its own both are very secure. If you combined PBKDF2 with an SHA256 as your salt then you'd have a very secure system.

Mr. Young
  • 2,198
  • 2
  • 24
  • 40
  • In this case phrase would be my password? – Emma Geller-Green Oct 26 '15 at 22:46
  • Yes. Phrase = "password" – Mr. Young Oct 26 '15 at 22:56
  • Quick question, let's say I have a WinForm, and it comes to the password box. I accept the password as a string and pass it to the phrase variable? – Emma Geller-Green Oct 27 '15 at 13:15
  • Isn't that a little insecure? – Emma Geller-Green Oct 27 '15 at 20:31
  • Are you familiar with Thunderbird(mail application) how do they store a users password locally? Here is what my goal is: http://crypto.stackexchange.com/questions/29845/properly-storing-an-e-mail-password – Emma Geller-Green Oct 27 '15 at 20:32
  • I am not sure how Thunderbird stores password securely as I am not familiar with the application. If values are coming in from the UI View, the only binding available is a string. So unless you did string scanning the instant that the user types in their password AND you aren't storing or transmitting the plain-text password you'll be alright in terms of security. I would of course put a password masking on the textbox. – Mr. Young Oct 27 '15 at 20:38
  • Thanks, one more thing, string scanning, you mean, check the string? – Emma Geller-Green Oct 27 '15 at 20:39
  • I mean scanning memory for strings. Tools like Process Explorer. But this discussion is getting too subjective for me. Please keep in mind the use guide of SO. http://stackoverflow.com/help/dont-ask – Mr. Young Oct 27 '15 at 20:41