Это немного сложный, но не слишком большой. Обратите внимание, что это для режима CBC + PKCS # 7!
Три метод: WriteStringToFile
создаст новый файл, AppendStringToFile
будет добавлять к уже зашифрованному файлу (работает как WriteStringToFile
если файл отсутствует/пустой), ReadStringFromFile
будет читать из файла.
public static void WriteStringToFile(string fileName, string plainText, byte[] key, byte[] iv)
{
using (Rijndael algo = Rijndael.Create())
{
algo.Key = key;
algo.IV = iv;
algo.Mode = CipherMode.CBC;
algo.Padding = PaddingMode.PKCS7;
// Create the streams used for encryption.
using (FileStream file = new FileStream(fileName, FileMode.Create, FileAccess.Write))
// Create an encryptor to perform the stream transform.
using (ICryptoTransform encryptor = algo.CreateEncryptor())
using (CryptoStream cs = new CryptoStream(file, encryptor, CryptoStreamMode.Write))
using (StreamWriter sw = new StreamWriter(cs))
{
// Write all data to the stream.
sw.Write(plainText);
}
}
}
public static void AppendStringToFile(string fileName, string plainText, byte[] key, byte[] iv)
{
using (Rijndael algo = Rijndael.Create())
{
algo.Key = key;
// The IV is set below
algo.Mode = CipherMode.CBC;
algo.Padding = PaddingMode.PKCS7;
// Create the streams used for encryption.
using (FileStream file = new FileStream(fileName, FileMode.OpenOrCreate, FileAccess.ReadWrite))
{
byte[] previous = null;
int previousLength = 0;
long length = file.Length;
// No check is done that the file is correct!
if (length != 0)
{
// The IV length is equal to the block length
byte[] block = new byte[iv.Length];
if (length >= iv.Length * 2)
{
// At least 2 blocks, take the penultimate block
// as the IV
file.Position = length - iv.Length * 2;
file.Read(block, 0, block.Length);
algo.IV = block;
}
else
{
// A single block present, use the IV given
file.Position = length - iv.Length;
algo.IV = iv;
}
// Read the last block
file.Read(block, 0, block.Length);
// And reposition at the beginning of the last block
file.Position = length - iv.Length;
// We use a MemoryStream because the CryptoStream
// will close the Stream at the end
using (var ms = new MemoryStream(block))
// Create a decrytor to perform the stream transform.
using (ICryptoTransform decryptor = algo.CreateDecryptor())
using (CryptoStream cs = new CryptoStream(ms, decryptor, CryptoStreamMode.Read))
{
// Read all data from the stream. The decrypted last
// block can be long up to block length characters
// (so up to iv.Length) (this with AES + CBC)
previous = new byte[iv.Length];
previousLength = cs.Read(previous, 0, previous.Length);
}
}
else
{
// Use the IV given
algo.IV = iv;
}
// Create an encryptor to perform the stream transform.
using (ICryptoTransform encryptor = algo.CreateEncryptor())
using (CryptoStream cs = new CryptoStream(file, encryptor, CryptoStreamMode.Write))
using (StreamWriter sw = new StreamWriter(cs))
{
// Rewrite the last block, if present. We even skip
// the case of block present but empty
if (previousLength != 0)
{
cs.Write(previous, 0, previousLength);
}
// Write all data to the stream.
sw.Write(plainText);
}
}
}
}
public static string ReadStringFromFile(string fileName, byte[] key, byte[] iv)
{
string plainText;
using (Rijndael algo = Rijndael.Create())
{
algo.Key = key;
algo.IV = iv;
algo.Mode = CipherMode.CBC;
algo.Padding = PaddingMode.PKCS7;
// Create the streams used for decryption.
using (FileStream file = new FileStream(fileName, FileMode.Open, FileAccess.Read))
// Create a decrytor to perform the stream transform.
using (ICryptoTransform decryptor = algo.CreateDecryptor())
using (CryptoStream cs = new CryptoStream(file, decryptor, CryptoStreamMode.Read))
using (StreamReader sr = new StreamReader(cs))
{
// Read all data from the stream.
plainText = sr.ReadToEnd();
}
}
return plainText;
}
Пример использования:
var key = Encoding.UTF8.GetBytes("Simple key");
var iv = Encoding.UTF8.GetBytes("Simple IV");
Array.Resize(ref key, 128/8);
Array.Resize(ref iv, 128/8);
if (File.Exists("test.bin"))
{
File.Delete("test.bin");
}
for (int i = 0; i < 100; i++)
{
AppendStringToFile("test.bin", string.Format("{0},", i), key, iv);
}
string plainText = ReadStringFromFile("test.bin", key, iv);
Как с AppendStringToFile
работает? Три случая:
- Пустой файл: в
WriteStringToFile
- Файл с одного блока: ХВ этого блока является IV передается в качестве параметра. Блок расшифровывается, а затем повторно шифруется вместе с переданным
plainText
- Файл с несколькими блоками: IV последнего блока является предпоследним блоком. Таким образом, предпоследний блок читается и используется как IV (IV передается при игнорировании параметра). Последний блок расшифровывается, а затем повторно зашифровывается вместе с переданным
plainText
. Чтобы повторно использовать последний блок, используемый IV является предпоследним блоком.
Большое спасибо за этот пример, я дам ему попробовать завтра. Один вопрос: будет ли это работать и с двоичными данными? У меня есть только что заменить StreamReader/StreamWriter на BinaryReader/BinaryWriter? Или есть что-то еще? Вопрос в том, могу ли я определить длину двоичных данных? Откуда я знаю, сколько байтов исходит из исходных данных и сколько добавочных байтов было добавлено, поэтому я могу удалить правильное количество байтов заполнения, чтобы добавить данные в правильное положение? –
@rudolf Вам не нужно отбрасывать байты заполнения, они лишаются 'CryptoStream' (или' ICryptoTransform', я не уверен), потому что вы используете «хорошее» дополнение – xanatos
@RudolfZiegaus У меня есть внесли некоторые изменения в метод Append, так что байты последнего блока, которые дешифрованы, не декодируются в поток, а просто повторно зашифрованы. – xanatos