I'm completely new to cryptography, but learning. I've pieced together many different suggestions from my research online, and have made my own class for handling the hash, salt, key stretching, and comparison/conversion of associated data.
After researching the built-in .NET library for cryptography, I discovered that what I have is still only SHA-1. But I'm coming to the conclusion that it's not bad since I'm using multiple iterations of the hash process. Is that correct?
But if I wanted to start with the 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've referenced a lot of practices from https://crackstation.net/hashing-security.htm. The slowEquals comparison method is to normalize execution time by preventing branching. The usage of SecureString is to have an encrypted form of the password pass between this class and other classes and pages within my web application. While this site will be over HTTPS, it's always nice to go the extra mile to ensure things are as secure as possible while still being within reason.
In my code, I've set the key string to 128 bytes (though it grows bigger sometimes, which is fine), the hash size to 1KB, and the number of iterations at 3,000. It's a little larger than the typical 64 byte salt, 512 byte hash, and 1,000 or 2,000 iterations, but then again login speed and app performance is an extremely low priority.
Thoughts?