C# 加密/解密大文件 (.NET)
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/9237324/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Encrypting/Decrypting large files (.NET)
提问by kalrashi
I have to encrypt, store and then later decrypt large files. What is the best way of doing that? I heard RSA encryption is expensive and was advised to use RSA to encrypt an AES key and then use the AES key to encrypt the large files. Any suggestions with example will be great.
我必须加密、存储然后解密大文件。这样做的最佳方法是什么?我听说 RSA 加密很昂贵,建议使用 RSA 加密 AES 密钥,然后使用 AES 密钥加密大文件。任何带有示例的建议都会很棒。
回答by Matt Dearing
Generally the strategy you have described is used when data will be encrypted on one machine (like a server) and then decrypted by another machine (client). The server will encrypt the data using symmetric key encryption (for performance) with a newly generated key and encrypt this symmetric key with a public key (matching a client's private key). The server sends the client both the encrypted data and the encrypted symmetric key. The client can decrypt the symmetric key with it's private key and then use this symmetric key for decrypting the data. If you are encrypting and decrypting the data on the same machine it may not make sense to use both RSA and AES as you would not be trying to pass the encryption key to another machine.
通常,当数据将在一台机器(如服务器)上加密,然后由另一台机器(客户端)解密时,通常会使用您所描述的策略。服务器将使用对称密钥加密(为了性能)使用新生成的密钥加密数据,并使用公钥(匹配客户端的私钥)加密此对称密钥。服务器向客户端发送加密数据和加密对称密钥。客户端可以用它的私钥解密对称密钥,然后使用这个对称密钥来解密数据。如果您在同一台机器上加密和解密数据,则同时使用 RSA 和 AES 可能没有意义,因为您不会尝试将加密密钥传递给另一台机器。
回答by HABO
One organism's large is another's petite, though we all know expensive when we see it. Wink, wink.
一个有机体的大是另一个有机体的娇小,尽管我们看到它时都知道它很贵。眨眼眨眼。
Try benchmarking something like the following in your environment and see where you're at:
尝试在您的环境中对以下内容进行基准测试,看看您所处的位置:
EDIT 2/13/2012: The code has been updated as I've become (imperceptibly) smarter and also noticed a few cut'n'paste errors that had crept in. Mea culpa.
2012 年 2 月 13 日编辑:代码已更新,因为我变得(不知不觉地)变得更聪明了,并且还注意到一些已经出现的剪切和粘贴错误。我的过错。
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);
}
}
}
}
回答by poupou
Like you heard asymmetric cryptography, like RSA, is much slower than symmetric cryptography (e.g. AES) but it does haveit's advantages (simpler key management, e.g. a single private key to protect).
就像您听说过的非对称密码学,如 RSA,比对称密码学(例如 AES)慢得多,但它确实有它的优势(更简单的密钥管理,例如要保护的单个私钥)。
The key (pun intended) is to use the advantages of both (private key of asymmetric and speed of symmetric) while ignoring the inconvenience of the other (many secret keys and slow speed).
密钥(双关语)是利用两者的优点(非对称的私钥和对称的速度),同时忽略另一个的不便(密钥多,速度慢)。
You can do this by using RSA once per file (no huge performance impact) to encrypt a (symmetric) secret key that is used to encrypt (much faster) your large file. This *wrappingof the symmetric key allows you to only manage a single, private key.
您可以通过对每个文件使用一次 RSA(不会对性能产生巨大影响)来加密用于加密(更快)大文件的(对称)密钥来实现此目的。对称密钥的这种 *包装允许您仅管理单个私钥。
Here's a link to my old (but still true) blog postthat gives an example to do this using C# and the .NET framework (Microsoft of Mono).
这是我旧的(但仍然是真实的)博客文章的链接,该文章给出了使用 C# 和 .NET 框架(Mono 的 Microsoft)执行此操作的示例。
回答by Muhammad Alaa
This may help
这可能有帮助
/// 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();
}
}
source: http://www.codeproject.com/Articles/26085/File-Encryption-and-Decryption-in-C
来源:http: //www.codeproject.com/Articles/26085/File-Encryption-and-Decryption-in-C
回答by Todd
RSA
RSA
It's true asymmetric cryptography (RSA, ECC, etc.) is slower than symmetric (AES, ChaCha20, etc). RSA and others are great for securing a random symmetric key (or establishing one). AES and others are great for efficient encryption, used along with integrity checking (HMAC).
真正的非对称加密(RSA、ECC 等)比对称加密(AES、ChaCha20 等)慢。RSA 和其他算法非常适合保护随机对称密钥(或建立一个)。AES 和其他算法非常适合与完整性检查 (HMAC) 一起使用的高效加密。
Importantly, mature symmetric ciphers don't have any known theoretical weakness. Unless your attackers has the symmetric key, the encryption cannot be broken. Currently, all mature asymmetric cryptography (RSA, ECC) are based on mathematical properties that are vulnerable to being cracked by a future Quantum Computer (if it ever comes).
重要的是,成熟的对称密码没有任何已知的理论弱点。除非您的攻击者拥有对称密钥,否则无法破解加密。目前,所有成熟的非对称加密(RSA、ECC)都基于数学特性,这些特性很容易被未来的量子计算机(如果有的话)破解。
Also, handling of public/private keys becomes a problem. It's simple for a human to remember a password - their brain cannot be hacked. With public/private keys, they need to be stored somewhere. Particularly the private key is sensitive. Computers have TDM components that can create and store public/private keys separate to the CPU. This is very complicated to use.
此外,处理公钥/私钥也成为一个问题。人类记住密码很简单——他们的大脑无法被黑客入侵。使用公钥/私钥,它们需要存储在某个地方。特别是私钥是敏感的。计算机具有 TDM 组件,可以创建和存储与 CPU 分开的公钥/私钥。这使用起来非常复杂。
So with that in mind, RSA should only be used if and when it's absolutely necessary.
因此,考虑到这一点,只有在绝对必要时才应使用 RSA。
AES
AES
Here is a complete version I wrote recently, that returns the wrapping streamer, so you can use it however you need.
这是我最近写的一个完整版本,它返回包装流光,因此您可以根据需要使用它。
Also, this method generates IV from random generator instead of the password digestor. This is best practice, for example 7z does this - see https://crypto.stackexchange.com/questions/61945/is-it-ok-to-transmit-an-iv-as-a-custom-http-header. The IV is included in the header for the output.
此外,此方法从随机生成器而不是密码摘要器生成 IV。这是最佳实践,例如 7z 这样做 - 请参阅https://crypto.stackexchange.com/questions/61945/is-it-ok-to-transmit-an-iv-as-a-custom-http-header。IV 包含在输出的标题中。
Usage:
用法:
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);
}
}
}
Utility:
公用事业:
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.
}
}

