private void FileEncrypt(string inputFile, string outputFile, string password)
{
//http://stackoverflow.com/questions/27645527/aes-encryption-on-large-files
//generate random salt
byte[] salt = GenerateRandomSalt();
FileStream fsCrypt = new FileStream(outputFile, FileMode.Create);
//convert password string to byte arrray
byte[] passwordBytes = System.Text.Encoding.UTF8.GetBytes(password);
//Set Rijndael symmetric encryption algorithm
RijndaelManaged AES = new RijndaelManaged();
AES.KeySize = 256;
AES.BlockSize = 128;
AES.Padding = PaddingMode.PKCS7;
//http://stackoverflow.com/questions/2659214/why-do-i-need-to-use-the-rfc2898derivebytes-class-in-net-instead-of-directly
//"What it does is repeatedly hash the user password along with the salt." High iteration counts.
// 보안성을 중시하면 다음 줄을 쓴다. 속도는 많이 느리다.
//var key = new Rfc2898DeriveBytes(passwordBytes, salt, 50000);
//AES.Key = key.GetBytes(AES.KeySize / 8);
//AES.IV = key.GetBytes(AES.BlockSize / 8);
// 속도를 중시하면 다음 줄을 쓴다. 속도는 빠르다.
AES.Key = Encoding.UTF8.GetBytes(password);
AES.IV = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
//Cipher modes: http://security.stackexchange.com/questions/52665/which-is-the-best-cipher-mode-and-padding-mode-for-aes-encryption
AES.Mode = CipherMode.CFB;
// write salt to the begining of the output file, so in this case can be random every time
fsCrypt.Write(salt, 0, salt.Length);
CryptoStream cs = new CryptoStream(fsCrypt, AES.CreateEncryptor(), CryptoStreamMode.Write);
FileStream fsIn = new FileStream(inputFile, FileMode.Open);
//create a buffer (1mb) so only this amount will allocate in the memory and not the whole file
byte[] buffer = new byte[1048576];
int read;
try
{
while ((read = fsIn.Read(buffer, 0, buffer.Length)) > 0)
{
Application.DoEvents(); // -> for responsive GUI, using Task will be better!
cs.Write(buffer, 0, read);
}
// Close up
fsIn.Close();
}
catch (Exception ex)
{
Console.WriteLine("Error: " + ex.Message);
}
finally
{
cs.Close();
fsCrypt.Close();
}
}
private void FileDecrypt(string inputFile, string outputFile, string password)
{
byte[] passwordBytes = System.Text.Encoding.UTF8.GetBytes(password);
byte[] salt = new byte[32];
FileStream fsCrypt = new FileStream(inputFile, FileMode.Open);
fsCrypt.Read(salt, 0, salt.Length);
RijndaelManaged AES = new RijndaelManaged();
AES.KeySize = 256;
AES.BlockSize = 128;
// 보안성을 중시하면 다음 줄을 쓴다. 속도는 많이 느리다.
//var key = new Rfc2898DeriveBytes(passwordBytes, salt, 50000);
//AES.Key = key.GetBytes(AES.KeySize / 8);
//AES.IV = key.GetBytes(AES.BlockSize / 8);
// 속도를 중시하면 다음 줄을 쓴다. 속도는 빠르다.
AES.Key = Encoding.UTF8.GetBytes(password);
AES.IV = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
AES.Padding = PaddingMode.PKCS7;
AES.Mode = CipherMode.CFB;
CryptoStream cs = new CryptoStream(fsCrypt, AES.CreateDecryptor(), CryptoStreamMode.Read);
FileStream fsOut = new FileStream(outputFile, FileMode.Create);
int read;
byte[] buffer = new byte[1048576];
try
{
while ((read = cs.Read(buffer, 0, buffer.Length)) > 0)
{
Application.DoEvents();
fsOut.Write(buffer, 0, read);
}
}
catch (CryptographicException ex_CryptographicException)
{
Console.WriteLine("CryptographicException error: " + ex_CryptographicException.Message);
}
catch (Exception ex)
{
Console.WriteLine("Error: " + ex.Message);
}
try
{
cs.Close();
}
catch (Exception ex)
{
Console.WriteLine("Error by closing CryptoStream: " + ex.Message);
}
finally
{
fsOut.Close();
fsCrypt.Close();
}
}
inputFile : 암호화할 파일 디렉토리
outputFile : 암호화되어 저장하기 위한 파일 디렉토리
password : 암호화 키
Rfc2898DeriveBytes의 GetBytes는 Key를 해쉬화하여 각 파일에 여러개의 Key를 주입한 효과를 준다. 그러나 보안성이 강화되는 만큼 너무 느리다..
var key = new Rfc2898DeriveBytes(passwordBytes, salt, 50000); 에서 50000을 100000을 넣으면 보안성이 더강화된다.
이 알고리즘을 사용하기 위해서는 사용클래스에
[DllImport("KERNEL32.DLL", EntryPoint = "RtlZeroMemory")]
public static extern bool ZeroMemory(IntPtr Destination, int Length);
/// <summary>
/// Creates a random salt that will be used to encrypt your file. This method is required on FileEncrypt.
/// salt : randomly string of byte unit
/// </summary>
/// <returns></returns>
private static byte[] GenerateRandomSalt()
{
byte[] data = new byte[32];
using (RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider())
{
for (int i = 0; i < 10; i++)
{
// Fille the buffer with the generated data
rng.GetBytes(data);
}
}
return data;
}
를 선언하고
public void EncryptFile(){
//AES256의 key는 32bit
string password = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
// For additional security Pin the password of your files
GCHandle gch = GCHandle.Alloc(password, GCHandleType.Pinned);
FileEncrypt(dirFilePath, encryptFilePath, password);
// To increase the security of the encryption, delete the given password from the memory !
ZeroMemory(gch.AddrOfPinnedObject(), password.Length * 2);
gch.Free();
}
public void DecryptFile(){
//AES256의 key는 32bit
string password = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
// For additional security Pin the password of your files
GCHandle gch = GCHandle.Alloc(password, GCHandleType.Pinned);
FileDecrypt(dirFilePath, decryptFilePath, password);
// To increase the security of the decryption, delete the used password from the memory !
ZeroMemory(gch.AddrOfPinnedObject(), password.Length * 2);
gch.Free();
}
다음과 같은 방식으로 쓰면 된다.