C# 将数据附加到加密文件
我想使用CryptoStream将数据附加到已经加密的文件(AES、CBC模式、填充PKCS#7),而不必读写整个文件 例如: 旧内容:C# 将数据附加到加密文件,c#,cryptostream,C#,Cryptostream,我想使用CryptoStream将数据附加到已经加密的文件(AES、CBC模式、填充PKCS#7),而不必读写整个文件 例如: 旧内容:“你好,世界…” 新内容:“Hello world,随附文本” 当然,我必须读取单独的数据块,然后附加到已经存在的块上。在上面提到的示例中,我必须读取第一个块中存在的字节数(14字节),并在第一个块中附加两个字节,然后写入附加文本的其余部分 "Hello world, wi" "th appended text" 我面临的一个问题是无法读取数据块中的字节数。有
“你好,世界…”
新内容:“Hello world,随附文本”
当然,我必须读取单独的数据块,然后附加到已经存在的块上。在上面提到的示例中,我必须读取第一个块中存在的字节数(14字节),并在第一个块中附加两个字节,然后写入附加文本的其余部分
"Hello world, wi"
"th appended text"
我面临的一个问题是无法读取数据块中的字节数。有没有办法找出存在的字节数(在示例中为14)
此外,我被卡住了,因为CryptoStreamMode只有读写成员,但没有更新
有没有一种方法可以使用CryptoStream实现我想要的功能?它有点复杂,但不是太多。请注意,这适用于CBC模式+PKCS#7 三种方法:
WriteStringToFile
将创建一个新文件,AppendStringToFile
将附加到一个已经加密的文件(如果文件丢失/为空,则作为WriteStringToFile
),将从文件中读取
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);
var key=Encoding.UTF8.GetBytes(“简单键”);
var iv=Encoding.UTF8.GetBytes(“简单iv”);
数组。调整大小(参考键,128/8);
数组。调整大小(参考iv,128/8);
如果(File.Exists(“test.bin”))
{
文件。删除(“test.bin”);
}
对于(int i=0;i<100;i++)
{
AppendStringToFile(“test.bin”,string.Format(“{0},”,i),键,iv);
}
字符串纯文本=ReadStringFromFile(“test.bin”,key,iv);
AppendStringToFile
是如何工作的?三个案例:
- 空文件:作为
WriteStringToFile
- 带有单个块的文件:该块的IV是作为参数传递的IV。该块被解密,然后与传递的
明文一起重新加密
- 具有多个块的文件:最后一个块的IV是倒数第二个块。因此,倒数第二个块被读取,并用作IV(作为参数传递的IV被忽略)。最后一个块被解密,然后与传递的
明文一起重新加密。要重新输入最后一个块,使用的IV是倒数第二个块
加密流
(或者ICryptoTransform
,我不确定),因为你用的是“好的”padding@RudolfZiegaus我对Append方法做了一些更改,这样最后一个解密块的字节就不会被解码成流,而是简单地重新加密。Helle xanatos,非常感谢你的回答。不幸的是,我没有时间更早地尝试它-它工作得很好!非常感谢你的帮助!