Skip to content

Instantly share code, notes, and snippets.

@smonn
Last active December 14, 2015 00:59
Show Gist options
  • Save smonn/5003278 to your computer and use it in GitHub Desktop.
Save smonn/5003278 to your computer and use it in GitHub Desktop.
Encrypt password with random salt, and example of slow equals.
/*
using System;
using System.Linq;
using System.Security.Cryptography;
*/
// The user's password in pure text.
var password = string.Empty;
// Generate a random salt, this should be stored in its own column in the database,
// preferably in a binary format (i.e. binary(32) in this case, adjust length accordingly).
// The salt should be at least as long as the generated hash, for SHA256 that is 32 bytes.
var rng = new RNGCryptoServiceProvider();
var salt = new byte[32];
// Basically, this will fill the salt array with random bytes.
rng.GetBytes(salt);
// Use at least SHA256 since SHA1 is very weak in comparison.
var sha256 = new SHA256CryptoServiceProvider();
// We don't care about encoding in this scenario, and using Buffer.BlockCopy saves
// us a tiny bit of memory and time over Encoding.XXX.GetBytes.
var passwordBytes = new byte[password.Length * sizeof(char)];
Buffer.BlockCopy(password.ToCharArray(), 0, passwordBytes, 0, passwordBytes.Length);
// Compute the password hash (this step is kind of optional, but the more we
// hash the more difficult it will be for attackers).
var hash = sha256.ComputeHash(passwordBytes);
// Prepend the salt and hash it all one more time. Mandatory step.
var bytes = sha256.ComputeHash(salt.Concat(hash).ToArray());
// What we have in the bytes variable should now be stored in the database in its own column.
// Just like the salt, store it in binary format.
// The reason for storing in binary format is basically to save us from having to convert the hash
// to a hexadecimal string and store that in the database (or worse, using Encoding.XXX.GetString).
// A simple way to calculate the hexadecimal string, however, would be to use something like this:
var hex = new string(bytes.SelectMany(x => x.ToString(@"X2").ToCharArray()).ToArray());
// Another option would be to use Convert.ToBase64String (a benefit with this approach is that
// we can convert it back to a byte array easily):
var base64 = Convert.ToBase64String(bytes);
// When comparing two byte arrays it is suggested to use a slow equals.
// This is done by comparing the entire array:
var a = bytes;
var b = bytes; // This should obivously be generated from a login attempt.
uint diff = (uint)a.Length ^ (uint)b.Length;
for (int i = 0; i < a.Length && i < b.Length; i++)
diff |= (uint)(a[i] ^ b[i]);
if (diff == 0)
Console.WriteLine("The passwords match!");
else
Console.WriteLine("Wrong password");
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment