c# - 在 .NET 中实现加密/解密流的正确方法?

标签 c# encryption stream file-format

我想导入和导出一个旧的游戏文件格式,它的数据是加密的。详情可见here ;简而言之,文件被分成多个 block ,每个 block 使用一种基于前一个 uint 的特定类型的 XOR 加密,并且校验和跟踪每个 block ,我在读取数据时需要跳过它。

通常,我想设计放置在游戏文件上的可重复使用的流,如果有一个流在后台进行加密/解密,而开发人员只使用 BinaryReader,那就太好了/Writer 做一些 ReadUInt32() 等事情

到目前为止,我研究过 .NET 中有一个 CryptoStream 类,实现加密/解密的“正确”方法是否从继承该类开始?我没有找到任何关于以这种方式尝试过的人的文章,因此我不确定我在这里是否完全错误。

最佳答案

虽然不是 C#,this MSDN page可能会提供一些见解,显示 ICryptoTransform interface 的实现.

这是一个在 C# 中可能看起来如何的示例,使用您在用例中提到的 XOR-with-previous-block(毫无疑问,您将不得不调整它以匹配您的确切算法):

using System;
using System.IO;
using System.Linq;
using System.Security.Cryptography;

class XORCryptoTransform : ICryptoTransform
{
    uint previous;
    bool encrypting;

    public XORCryptoTransform(uint iv, bool encrypting)
    {
        previous = iv;
        this.encrypting = encrypting;
    }

    public int TransformBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset)
    {
        for (int i = 0; i < inputCount; i+=4)
        {
            uint block = BitConverter.ToUInt32(inputBuffer, inputOffset + i);
            byte[] transformed = BitConverter.GetBytes(block ^ previous);
            Array.Copy(transformed, 0, outputBuffer, outputOffset + i, Math.Min(transformed.Length, outputBuffer.Length - outputOffset -i));

            if (encrypting)
            {
                previous = block;
            }
            else
            {
                previous = BitConverter.ToUInt32(transformed, 0);
            }
        }

        return inputCount;
    }

    public byte[] TransformFinalBlock(byte[] inputBuffer, int inputOffset, int inputCount)
    {
        var transformed = new byte[inputCount];
        TransformBlock(inputBuffer, inputOffset, inputCount, transformed, 0);
        return transformed;
    }

    public bool CanReuseTransform
    {
        get { return true; }
    }

    public bool CanTransformMultipleBlocks
    {
        get { return true; }
    }

    public int InputBlockSize
    {
        // 4 bytes in uint
        get { return 4; }
    }

    public int OutputBlockSize
    {
        get { return 4; }
    }

    public void Dispose()
    {
    }
}

class Program
{
    static void Main()
    {
        uint iv = 0; // first block will not be changed
        byte[] plaintext = Guid.NewGuid().ToByteArray();
        byte[] ciphertext;
        using (var memoryStream = new MemoryStream())
        {
            using (var encryptStream = new CryptoStream(
                memoryStream,
                new XORCryptoTransform(iv, true),
                CryptoStreamMode.Write))
            {
                encryptStream.Write(plaintext, 0, plaintext.Length);
            }

            ciphertext = memoryStream.ToArray();
        }

        byte[] decrypted = new byte[ciphertext.Length];

        using (var memoryStream = new MemoryStream(ciphertext))
        using (var encryptStream = new CryptoStream(
                memoryStream,
                new XORCryptoTransform(iv, false),
                CryptoStreamMode.Read))
        {
            encryptStream.Read(decrypted, 0, decrypted.Length);
        }

        bool matched = plaintext.SequenceEqual(decrypted);
        Console.WriteLine("Matched: {0}", matched);
    }
}

在此示例中,如果输入数据是 block 长度的倍数(在您的情况下为 uint 为 4 个字节),则在 TransformFinalBlock 中无需执行任何操作。 .但是,如果数据不是 block 长度的倍数,则剩余的字节将在那里处理。

.NET 自动填充传递给 TransformFinalBlock 的数组用零来达到 block 长度,但你可以通过检查 inputCount 来检测它(这将是实际输入长度,而不是填充长度)并在您的算法需要时替换为您自己的自定义(非零)填充。

关于c# - 在 .NET 中实现加密/解密流的正确方法?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28927265/

相关文章:

c# - 从 Windows Phone 7 读取文件

c# - 为什么 EF 说我的模型已更改,而实际上它没有更改?

c# - 正则表达式嵌套括号在一种情况下不起作用

java - 我应该为每列使用不同的键吗?

encryption - 如何识别密文中使用的加密算法?

java - 将流转换为路径/文件名而不写入物理磁盘

c# - 剑道网格单元格编辑

c# - 使用 CorrelationManager 链接多个 TraceSource 事件

c# - Foreach循环检查空字符串并为其赋值c#

java - 字符和字节流转换为字符的区别 对于可以用 1byte 表示的字符