I have a C# application which needs to encrypt string and save the key as a hash. The key is saved as a hash to check the right key has been input before trying to decrypt the message.
First, I have these method to generate the hash string - hopefully "off the shelf" and ready to go
public static byte[] GetHash(string inputString)
{
using (HashAlgorithm algorithm = SHA256.Create())
return algorithm.ComputeHash(Encoding.UTF8.GetBytes(inputString));
}
public static string GetHashString(string inputString)
{
StringBuilder sb = new StringBuilder();
foreach (byte b in GetHash(inputString))
sb.Append(b.ToString("X2"));
return sb.ToString();
}
Then my encryption extension method:
private const int Keysize = 256;
private const int DerivationIterations = 1000;
public static string Encrypt(this string plainText, string passPhrase)
{
// Salt and IV is randomly generated each time, but is preprended to encrypted cipher text
// so that the same Salt and IV values can be used when decrypting.
var saltStringBytes = Generate256BitsOfRandomEntropy();
var ivStringBytes = Generate256BitsOfRandomEntropy();
var plainTextBytes = Encoding.UTF8.GetBytes(plainText);
using (var password = new Rfc2898DeriveBytes(passPhrase, saltStringBytes, DerivationIterations))
{
var keyBytes = password.GetBytes(Keysize / 8);
var engine = new RijndaelEngine(256);
var blockCipher = new CbcBlockCipher(engine);
var cipher = new PaddedBufferedBlockCipher(blockCipher, new Pkcs7Padding());
var keyParam = new KeyParameter(keyBytes);
var keyParamWithIV = new ParametersWithIV(keyParam, ivStringBytes, 0, 32);
cipher.Init(true, keyParamWithIV);
var comparisonBytes = new byte[cipher.GetOutputSize(plainTextBytes.Length)];
var length = cipher.ProcessBytes(plainTextBytes, comparisonBytes, 0);
cipher.DoFinal(comparisonBytes, length);
// return Convert.ToBase64String(comparisonBytes);
return Convert.ToBase64String(saltStringBytes.Concat(ivStringBytes).Concat(comparisonBytes).ToArray());
}
}
private static byte[] Generate256BitsOfRandomEntropy()
{
var randomBytes = new byte[32]; // 32 Bytes will give us 256 bits.
using (var rngCsp = new RNGCryptoServiceProvider())
{
// Fill the array with cryptographically secure random bytes.
rngCsp.GetBytes(randomBytes);
}
return randomBytes;
}
Which is then called to a input string:
var encryptedString = inputString.Encrypt(_appSettings.EncryptionSecret + passPhrase);
The passphrase is a random set of characters, prepended with an application string.
My concern is that the passphrase is 4 sets of "diceware" words (like here: https://diceware.dmuth.org/) and therefore could be brute forced. Would adding more to the passphrase
, such as as hash of DateTime.UtcNow.ToString("yyyyMMdd")
for example add any additional security or is it just theatre and the "diceware" passphrase is practicably unbreakable?