I am completely new to cryptography, but learning. I have compiled many different suggestions from my research on the Internet and made my own class for handling hash, salt, key stretching and comparing / converting related data.
After exploring the .NET built-in cryptography library, I found that I only have SHA-1. But I come to the conclusion that this is not bad, since I use several iterations of the hash process. It is right?
But if I wanted to start with a more robust SHA-512, how could I implement it in my code below? Thanks in advance.
using System; using System.Runtime.InteropServices; using System.Security; using System.Security.Cryptography; public class CryptoSaltAndHash { private string strHash; private string strSalt; public const int SaltSizeInBytes = 128; public const int HashSizeInBytes = 1024; public const int Iterations = 3000; public string Hash { get { return strHash; } } public string Salt { get { return strSalt; } } public CryptoSaltAndHash(SecureString ThisPassword) { byte[] bytesSalt = new byte[SaltSizeInBytes]; using (RNGCryptoServiceProvider crypto = new RNGCryptoServiceProvider()) { crypto.GetBytes(bytesSalt); } strSalt = Convert.ToBase64String(bytesSalt); strHash = ComputeHash(strSalt, ThisPassword); } public static string ComputeHash(string ThisSalt, SecureString ThisPassword) { byte[] bytesSalt = Convert.FromBase64String(ThisSalt); Rfc2898DeriveBytes pbkdf2 = new Rfc2898DeriveBytes( convertSecureStringToString(ThisPassword), bytesSalt, Iterations); using (pbkdf2) { return Convert.ToBase64String(pbkdf2.GetBytes(HashSizeInBytes)); } } public static bool Verify(string ThisSalt, string ThisHash, SecureString ThisPassword) { if (slowEquals(getBytes(ThisHash), getBytes(ComputeHash(ThisSalt, ThisPassword)))) { return true; } return false; } private static string convertSecureStringToString(SecureString MySecureString) { IntPtr ptr = IntPtr.Zero; try { ptr = Marshal.SecureStringToGlobalAllocUnicode(MySecureString); return Marshal.PtrToStringUni(ptr); } finally { Marshal.ZeroFreeGlobalAllocUnicode(ptr); } } private static bool slowEquals(byte[] A, byte[] B) { int intDiff = A.Length ^ B.Length; for (int i = 0; i < A.Length && i < B.Length; i++) { intDiff |= A[i] ^ B[i]; } return intDiff == 0; } private static byte[] getBytes(string MyString) { byte[] b = new byte[MyString.Length * sizeof(char)]; System.Buffer.BlockCopy(MyString.ToCharArray(), 0, b, 0, b.Length); return b; } }
Notes. I referred to many practices from https://crackstation.net/hashing-security.htm . The slowEquals comparison method is to normalize runtime by preventing branching. Using SecureString is to have an encrypted form of password pass between this class and other classes and pages in my web application. Although this site will depend on HTTPS, itโs always nice to go the extra mile to ensure security as much as possible, although itโs within reason.
In my code, I set the key string to 128 bytes (although sometimes it increases, which is great), the hash size is up to 1 KB and the number of iterations is 3000. This is slightly larger than the typical 64-byte salt, 512-byte hash and 1000 or 2 000 iterations, but then again the input speed and application performance are extremely low.
Thoughts?