首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >将数据附加到加密文件

将数据附加到加密文件
EN

Stack Overflow用户
提问于 2015-06-16 09:04:00
回答 1查看 2K关注 0票数 3

我想将数据添加到一个已经加密的文件(AES,CBC-Mode,填充PKCS#7)中,使用CryptoStream,而不用读取和写入整个文件。

示例:

旧内容:"Hello world..."

新内容:"Hello world, with appended text"

当然,我必须读取单个数据块,然后追加到一个已经存在的块中。在上面提到的示例中,我必须读取第一个块中的字节数(14个字节),并将两个字节追加到第一个块中,然后编写所附文本的其余部分。

代码语言:javascript
复制
"Hello world, wi"
"th appended text"

我面临的一个问题是,我无法读取数据块中的字节数。有没有办法找出存在的字节数(在示例中是14)?

另外,由于CryptoStreamMode只具有读和写成员,而没有更新,所以我被困住了。

有什么方法可以使用CryptoStream完成我想要的功能吗?

EN

回答 1

Stack Overflow用户

发布于 2015-06-16 09:50:05

这有点复杂,但不算太复杂。请注意,这是用于CBC模式+ PKCS#7!

三种方法:WriteStringToFile将创建一个新文件,AppendStringToFile将附加到一个已经加密的文件(如果该文件丢失/空,则作为WriteStringToFile工作),ReadStringFromFile将从该文件中读取。

代码语言:javascript
复制
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;
}

使用实例:

代码语言:javascript
复制
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是如何工作的?三起案件:

  • 空文件: as WriteStringToFile
  • 带有单个块的文件:该块的IV是作为参数传递的IV。对块进行解密,然后与传递的plainText一起重新加密。
  • 包含多个块的文件:最后一个块的IV是倒数第二个块。因此,读取倒数第二个块,并将其用作IV (忽略作为参数传递的IV )。最后一个块被解密,然后与传递的plainText一起重新加密。对于最后一个区块,使用的IV是倒数第二个区块。
票数 7
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/30863146

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档