C# 加密/解密大型文件(.NET)
我必须加密、存储然后在以后解密大型文件。最好的方法是什么?我听说RSA加密非常昂贵,建议使用RSA加密AES密钥,然后使用AES密钥加密大型文件。任何有范例的建议都会很好 通常,当数据在一台机器(如服务器)上加密,然后由另一台机器(客户端)解密时,会使用您描述的策略。服务器将使用新生成的密钥使用对称密钥加密(为了性能)对数据进行加密,并使用公钥(与客户端的私钥匹配)对该对称密钥进行加密。服务器向客户端发送加密数据和加密对称密钥。客户端可以使用其私钥解密对称密钥,然后使用此对称密钥解密数据。C# 加密/解密大型文件(.NET),c#,.net,encryption,cryptography,rsa,C#,.net,Encryption,Cryptography,Rsa,我必须加密、存储然后在以后解密大型文件。最好的方法是什么?我听说RSA加密非常昂贵,建议使用RSA加密AES密钥,然后使用AES密钥加密大型文件。任何有范例的建议都会很好 通常,当数据在一台机器(如服务器)上加密,然后由另一台机器(客户端)解密时,会使用您描述的策略。服务器将使用新生成的密钥使用对称密钥加密(为了性能)对数据进行加密,并使用公钥(与客户端的私钥匹配)对该对称密钥进行加密。服务器向客户端发送加密数据和加密对称密钥。客户端可以使用其私钥解密对称密钥,然后使用此对称密钥解密数据。 如果
如果您在同一台机器上加密和解密数据,那么同时使用RSA和AES可能没有意义,因为您不会试图将加密密钥传递给另一台机器。一个有机体的大是另一个有机体的小,尽管我们看到它时都知道它很昂贵。眨眼,眨眼 尝试在您的环境中进行如下基准测试,看看您的情况: 《编辑》2012年2月13日:代码已经更新,因为我(不知不觉地)变得更聪明了,并且注意到一些剪切粘贴错误已经悄悄出现。我有罪
使用系统;
使用System.IO;
使用System.Security.Cryptography;
使用系统文本;
...
//Rfc2898DeriveBytes常量:
公共只读字节[]salt=新字节[]{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00};//必须至少为8个字节。把这个加盐!
公共常量int迭代次数=1042;//建议大于等于1000。
///解密一个文件。
///注意:“填充无效,无法删除。”是Universal CryptoServices错误。在紧张之前,确保密码、salt和迭代是正确的。
///要解密的文件的完整路径和名称。
///要输出的文件的完整路径和名称。
///解密的密码。
///要应用于密码的salt。
///在为解密生成密钥和初始化向量之前,应使用Rfc2898DeriveBytes的迭代次数。
公共无效解密文件(字符串sourceFilename、字符串destinationFilename、字符串密码、字节[]salt、int迭代次数)
{
aes管理aes=新aes管理();
aes.BlockSize=aes.LegalBlockSize[0].MaxSize;
aes.KeySize=aes.LegalKeySizes[0].MaxSize;
//注意:Rfc2898DeriveBytes初始化和对GetBytes的后续调用在加密和解密端都必须完全相同,包括顺序。
Rfc2898DeriveBytes键=新的Rfc2898DeriveBytes(密码、salt、迭代次数);
aes.Key=Key.GetBytes(aes.KeySize/8);
aes.IV=key.GetBytes(aes.BlockSize/8);
aes.Mode=CipherMode.CBC;
ICryptoTransform transform=aes.CreateDecryptor(aes.Key,aes.IV);
使用(FileStream destination=newfilestream(destinationFilename,FileMode.CreateNew,FileAccess.Write,FileShare.None))
{
使用(CryptoStream CryptoStream=新的CryptoStream(目标、转换、CryptoStreamMode.Write))
{
尝试
{
使用(FileStream source=newfilestream(sourceFilename,FileMode.Open,FileAccess.Read,FileShare.Read))
{
source.CopyTo(加密流);
}
}
捕获(加密异常)
{
if(exception.Message==“填充无效,无法删除。”)
抛出新的ApplicationException(“通用Microsoft加密异常(不可信!)”,异常;
其他的
投掷;
}
}
}
}
///加密文件。
///要加密的文件的完整路径和名称。
///要输出的文件的完整路径和名称。
///加密的密码。
///要应用于密码的salt。
///在为解密生成密钥和初始化向量之前,应使用Rfc2898DeriveBytes的迭代次数。
public void EncryptFile(字符串sourceFilename、字符串destinationFilename、字符串密码、字节[]salt、int迭代次数)
{
aes管理aes=新aes管理();
aes.BlockSize=aes.LegalBlockSize[0].MaxSize;
aes.KeySize=aes.LegalKeySizes[0].MaxSize;
//注意:Rfc2898DeriveBytes初始化和对GetBytes的后续调用在加密和解密端都必须完全相同,包括顺序。
Rfc2898DeriveBytes键=新的Rfc2898DeriveBytes(密码、salt、迭代次数);
aes.Key=Key.GetBytes(aes.KeySize/8);
aes.IV=key.GetBytes(aes.BlockSize/8);
aes.Mode=CipherMode.CBC;
ICryptoTransform transform=aes.CreateEncryptor(aes.Key,aes.IV);
使用(FileStream destination=newfilestream(destinationFilename,FileMode.CreateNew,FileAccess.Write,FileShare.None))
{
使用(CryptoStream CryptoStream=新的CryptoStream(目标、转换、CryptoStreamMode.Write))
{
使用(FileStream source=newfilestream(sourceFilename,FileMode.Open,FileAccess.Read,FileShare.Read))
{
source.CopyTo(加密流);
}
}
}
}
正如您所听到的,非对称加密(如RSA)比对称加密(如AES)慢得多,但它确实有它的优点(更简单的密钥管理,如保护单个私钥)
密钥(双关语的目的)是利用两者的优点(非对称私钥和对称速度)
using System;
using System.IO;
using System.Security.Cryptography;
using System.Text;
...
// Rfc2898DeriveBytes constants:
public readonly byte[] salt = new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; // Must be at least eight bytes. MAKE THIS SALTIER!
public const int iterations = 1042; // Recommendation is >= 1000.
/// <summary>Decrypt a file.</summary>
/// <remarks>NB: "Padding is invalid and cannot be removed." is the Universal CryptoServices error. Make sure the password, salt and iterations are correct before getting nervous.</remarks>
/// <param name="sourceFilename">The full path and name of the file to be decrypted.</param>
/// <param name="destinationFilename">The full path and name of the file to be output.</param>
/// <param name="password">The password for the decryption.</param>
/// <param name="salt">The salt to be applied to the password.</param>
/// <param name="iterations">The number of iterations Rfc2898DeriveBytes should use before generating the key and initialization vector for the decryption.</param>
public void DecryptFile(string sourceFilename, string destinationFilename, string password, byte[] salt, int iterations)
{
AesManaged aes = new AesManaged();
aes.BlockSize = aes.LegalBlockSizes[0].MaxSize;
aes.KeySize = aes.LegalKeySizes[0].MaxSize;
// NB: Rfc2898DeriveBytes initialization and subsequent calls to GetBytes must be eactly the same, including order, on both the encryption and decryption sides.
Rfc2898DeriveBytes key = new Rfc2898DeriveBytes(password, salt, iterations);
aes.Key = key.GetBytes(aes.KeySize / 8);
aes.IV = key.GetBytes(aes.BlockSize / 8);
aes.Mode = CipherMode.CBC;
ICryptoTransform transform = aes.CreateDecryptor(aes.Key, aes.IV);
using (FileStream destination = new FileStream(destinationFilename, FileMode.CreateNew, FileAccess.Write, FileShare.None))
{
using (CryptoStream cryptoStream = new CryptoStream(destination, transform, CryptoStreamMode.Write))
{
try
{
using (FileStream source = new FileStream(sourceFilename, FileMode.Open, FileAccess.Read, FileShare.Read))
{
source.CopyTo(cryptoStream);
}
}
catch (CryptographicException exception)
{
if (exception.Message == "Padding is invalid and cannot be removed.")
throw new ApplicationException("Universal Microsoft Cryptographic Exception (Not to be believed!)", exception);
else
throw;
}
}
}
}
/// <summary>Encrypt a file.</summary>
/// <param name="sourceFilename">The full path and name of the file to be encrypted.</param>
/// <param name="destinationFilename">The full path and name of the file to be output.</param>
/// <param name="password">The password for the encryption.</param>
/// <param name="salt">The salt to be applied to the password.</param>
/// <param name="iterations">The number of iterations Rfc2898DeriveBytes should use before generating the key and initialization vector for the decryption.</param>
public void EncryptFile(string sourceFilename, string destinationFilename, string password, byte[] salt, int iterations)
{
AesManaged aes = new AesManaged();
aes.BlockSize = aes.LegalBlockSizes[0].MaxSize;
aes.KeySize = aes.LegalKeySizes[0].MaxSize;
// NB: Rfc2898DeriveBytes initialization and subsequent calls to GetBytes must be eactly the same, including order, on both the encryption and decryption sides.
Rfc2898DeriveBytes key = new Rfc2898DeriveBytes(password, salt, iterations);
aes.Key = key.GetBytes(aes.KeySize / 8);
aes.IV = key.GetBytes(aes.BlockSize / 8);
aes.Mode = CipherMode.CBC;
ICryptoTransform transform = aes.CreateEncryptor(aes.Key, aes.IV);
using (FileStream destination = new FileStream(destinationFilename, FileMode.CreateNew, FileAccess.Write, FileShare.None))
{
using (CryptoStream cryptoStream = new CryptoStream(destination, transform, CryptoStreamMode.Write))
{
using (FileStream source = new FileStream(sourceFilename, FileMode.Open, FileAccess.Read, FileShare.Read))
{
source.CopyTo(cryptoStream);
}
}
}
}
/// Encrypts a file using Rijndael algorithm.
///</summary>
///<param name="inputFile"></param>
///<param name="outputFile"></param>
private void EncryptFile(string inputFile, string outputFile)
{
try
{
string password = @"myKey123"; // Your Key Here
UnicodeEncoding UE = new UnicodeEncoding();
byte[] key = UE.GetBytes(password);
string cryptFile = outputFile;
FileStream fsCrypt = new FileStream(cryptFile, FileMode.Create);
RijndaelManaged RMCrypto = new RijndaelManaged();
CryptoStream cs = new CryptoStream(fsCrypt,
RMCrypto.CreateEncryptor(key, key),
CryptoStreamMode.Write);
FileStream fsIn = new FileStream(inputFile, FileMode.Open);
int data;
while ((data = fsIn.ReadByte()) != -1)
cs.WriteByte((byte)data);
fsIn.Close();
cs.Close();
fsCrypt.Close();
}
catch
{
MessageBox.Show("Encryption failed!", "Error");
}
}
///
/// Decrypts a file using Rijndael algorithm.
///</summary>
///<param name="inputFile"></param>
///<param name="outputFile"></param>
private void DecryptFile(string inputFile, string outputFile)
{
{
string password = @"myKey123"; // Your Key Here
UnicodeEncoding UE = new UnicodeEncoding();
byte[] key = UE.GetBytes(password);
FileStream fsCrypt = new FileStream(inputFile, FileMode.Open);
RijndaelManaged RMCrypto = new RijndaelManaged();
CryptoStream cs = new CryptoStream(fsCrypt,
RMCrypto.CreateDecryptor(key, key),
CryptoStreamMode.Read);
FileStream fsOut = new FileStream(outputFile, FileMode.Create);
int data;
while ((data = cs.ReadByte()) != -1)
fsOut.WriteByte((byte)data);
fsOut.Close();
cs.Close();
fsCrypt.Close();
}
}
void Save()
{
var encryptedFilePath = Directory.GetCurrentDirectory() + "\\data.bin.aes";
using(var fileStream = File.Create(encryptedFilePath))
{
using (var cryptoStream = Security.FileEncryptor.CreateEncryptor(fileStream, passwordHere))
{
var formatter = new BinaryFormatter();
formatter.Serialize(cryptoStream, myObject);
cryptoStream.Flush();
}
}
}
void Load()
{
var encryptedFilePath = Directory.GetCurrentDirectory() + "\\data.bin.aes";
using(var fileStream = File.Open(encryptedFilePath, FileMode.Open))
{
using (var cryptoStream = Security.FileEncryptor.CreateDecryptor(fileStream, passwordHere))
{
var formatter = new BinaryFormatter();
var myObject = (myObjectType)formatter.Deserialize(cryptoStream);
}
}
}
using System.IO;
using System.Security.Cryptography;
using System;
namespace Security
{
class FileEncryptor
{
public static Stream CreateEncryptor(Stream source, string password)
{
byte[] SaltBytes = new byte[16];
RandomNumberGenerator.Fill(SaltBytes); //RandomNumberGenerator is used for .Net Core 3
AesManaged aes = new AesManaged();
aes.BlockSize = aes.LegalBlockSizes[0].MaxSize;
aes.KeySize = aes.LegalKeySizes[0].MaxSize;
Rfc2898DeriveBytes key = new Rfc2898DeriveBytes(password, SaltBytes, iterations);
aes.Key = key.GetBytes(aes.KeySize / 8);
byte[] IVBytes = new byte[aes.BlockSize / 8];
RandomNumberGenerator.Fill(IVBytes); //RandomNumberGenerator is used for .Net Core 3
aes.IV = IVBytes;
aes.Mode = CipherMode.CBC;
ICryptoTransform transform = aes.CreateEncryptor(aes.Key, aes.IV);
//Store/Send the Salt and IV - this can be shared. It's more important that it's very random, than being private.
source.WriteByte((byte)SaltBytes.Length);
source.Write(SaltBytes, 0, SaltBytes.Length);
source.WriteByte((byte)IVBytes.Length);
source.Write(IVBytes, 0, IVBytes.Length);
source.Flush();
var cryptoStream = new CryptoStream(source, transform, CryptoStreamMode.Write);
return cryptoStream;
}
public static Stream CreateDecryptor(Stream source, string password)
{
var ArrayLength = source.ReadByte();
if (ArrayLength == -1) throw new Exception("Salt length not found");
byte[] SaltBytes = new byte[ArrayLength];
var readBytes = source.Read(SaltBytes, 0, ArrayLength);
if (readBytes != ArrayLength) throw new Exception("No support for multiple reads");
ArrayLength = source.ReadByte();
if (ArrayLength == -1) throw new Exception("Salt length not found");
byte[] IVBytes = new byte[ArrayLength];
readBytes = source.Read(IVBytes, 0, ArrayLength);
if (readBytes != ArrayLength) throw new Exception("No support for multiple reads");
AesManaged aes = new AesManaged();
aes.BlockSize = aes.LegalBlockSizes[0].MaxSize;
aes.KeySize = aes.LegalKeySizes[0].MaxSize;
aes.IV = IVBytes;
Rfc2898DeriveBytes key = new Rfc2898DeriveBytes(password, SaltBytes, iterations);
aes.Key = key.GetBytes(aes.KeySize / 8);
aes.Mode = CipherMode.CBC;
ICryptoTransform transform = aes.CreateDecryptor(aes.Key, aes.IV);
var cryptoStream = new CryptoStream(source, transform, CryptoStreamMode.Read);
return cryptoStream;
}
public const int iterations = 1042; // Recommendation is >= 1000.
}
}