I'm working on a simple utility to encrypt files as a learning experience. Everything seems to be working fine, but I'm wondering if I have setup the Key/IV/Salt data securely.
I have noticed that when it comes to cryptography most people seem to envision a working environment that is loaded with malware manned by a wizard remotely ready to dive through the memory of a running application/page file to get at these secure files.
Let's pretend that you're on a clean machine and you encrypt some files and turn off your computer. What I want to know is whether or not someone can take your hard drive and retrieve the contents of the files using the code I have proposed.
The attack vector I am most concerned with is ensuring that the page files/file caches are inaccessible. I also want to make sure that the Key/IV system used is not going to make a rainbow table/hash based attack feasible.
Entering the Password:
The password is entered using a text box with the passwordchar value set to true.
I'm not really concerned with the string being in memory as long as it is properly removed after the encryption. I read that using SecureString is kind of pointless at this point because if you have malware on your computer already, you could just as easily have a keylogger on there which renders everything else useless.
private static string salt = "02341235XadfaDADFexA8932F7Dz3J3X";
I salt the password using a hard coded 32 character string. (The above string is just an example.)
To get at it, it will require someone to decompile/view the .exe file itself with a hex editor (something that I know if very easy to do, but an extra step nonetheless).
I have considered making this salt editable, but I'm not sure how I could securely store it. I think it's a little ridiculous to encrypt your salt because then you will have the same issue etc, so just leaving it as a hard coded string inside the exe itself seems to make the most sense to me.
The way this works is if you decide to make your password "thepassword", it is actually saved as "thepasswordfaDADFexA8932F7Dz3J3X".
The main key here is that you always have a 32 character password, regardless of what you enter.
The Key and IV:
The Key and IV are also salted as follows. This is what I wanted to get some input on, because to be honest I'm not entirely sure what it's doing:
UnicodeEncoding UE = new UnicodeEncoding();
byte[] keysalt = UE.GetBytes("Xjafe231x42X423XadXCadfkhjeAdS"); //Another string of random characters hard coded in the exe
byte[] IVSodium = UE.GetBytes("83Xkda7l78Dkx85KdJazppoqq6SaxDs"); //Another string of random characters hard coded in the exe
byte[] key = new Rfc2898DeriveBytes(password, keysalt).GetBytes(32); //Derive the key using the password and salt
byte[] IV = new Rfc2898DeriveBytes(password, IVSodium).GetBytes(16); //Derive the IV using the password and salt
My main concern here is that the IV is based on the key. Again, I'm not sure if this will cause any issues and I was hoping you guys could let me know if there are issues, what they are.
Also, is this another scenario where hard coding the salt is a bad practice? Should this be stored in the encrypted file, and if so, does it really make it more secure? Should I make this editable as well?
The crypto streams are setup using the using keyword:
using (FileStream fsCrypt = new FileStream(cryptFile, FileMode.Create))
{
using (RijndaelManaged RMCrypto = new RijndaelManaged())
{
using (CryptoStream cs = new CryptoStream(fsCrypt, RMCrypto.CreateEncryptor(key, IV), CryptoStreamMode.Write))
{
using (FileStream fsIn = new FileStream(inputFile, FileMode.Open))
{
byte[] buffer = new byte[4096]; //4096 is kind of arbitrary - better idea?
int data;
long bytesRead = 0;
while((data = fsIn.Read(buffer, 0, buffer.Length)) > 0)
{
bytesRead += data;
/////////////////////////////////////////
// Handle Aborts and Update Progress Bar
/////////////////////////////////////////
if (!caller.isClosing)
caller.Invoke((MethodInvoker)delegate {
caller.fileProgressBar.Value = ((int)(((double)bytesRead / totalBytes) * 100));
});
else
return false; //Encryption Aborted
/////////////////////////////////////////
cs.Write(buffer, 0, data);
fsIn.Close();
cs.Close();
fsCrypt.Close();
return true;
}
}
}
}
}
Thanks for your time and please let me know if there is a better way to setup the Key/IV/Salt. I think that it is most likely secure enough as long as there is not a mathematical issue with the IV and Key containing similar characters. If so, should I use a hard coded IV as well? That seems weird.
Note that I'm not saving a hash of the password or anything like that. The password is not saved anywhere. It is just used to generate the Key and the IV.
Thanks for your time.
Edit: Here are the changes recommended for anyone looking in the future.
Note that this is not using a pepper - just a random salt, although it would be easy enough to add
byte[] salt = new byte[32]; //Create a 32 byte salt
rand.NextBytes(salt); //Fill it with random values (use RandomNumberGenerator rand = new RNGCryptoServiceProvider(); to be safe
byte[] IV = new byte[16]; //Create a 16 byte IV
rand.NextBytes(IV); //Fill it with random numbers
byte[] key = new Rfc2898DeriveBytes(password, salt).GetBytes(32); //Derive our Key by mixing our password with the salt
using (FileStream fsCrypt = new FileStream(cryptFile, FileMode.Create))
{
using (RijndaelManaged RMCrypto = new RijndaelManaged())
{
using (CryptoStream cs = new CryptoStream(fsCrypt, RMCrypto.CreateEncryptor(key, IV), CryptoStreamMode.Write))
{
using (FileStream fsIn = new FileStream(inputFile, FileMode.Open))
{
fsCrypt.Write(salt, 0, salt.Length); //Write our salt to the file
fsCrypt.Write(IV, 0, IV.Length); //Write our IV to the file
fsIn.CopyTo(cs); //Encrypt and Write
}
}
}
}