在.NET中实现en / decrypting流的正确方法?

时间:2015-03-08 13:59:41

标签: c# encryption stream file-format

我想导入和导出旧的游戏文件格式,并且其数据已加密。找到详细信息here;简而言之,文件被分成块,每个块使用基于先前uint的特定种类的XOR加密,并且校验和跟踪我在读取数据时需要跳过的每个块。

通常,我想设计可以重复使用的游戏文件流,如果有一个流在后台进行加密/解密,那么开发人员只需使用{ {1}}做一些BinaryReader/Writer等事情。

所以我到目前为止我研究过.NET中有一个ReadUInt32()类,"是否正确"实现en / decryption的方法从继承该类开始?我没有找到关于那种尝试过这种方式的人的文章,因此我不确定我是否完全错了。

2 个答案:

答案 0 :(得分:4)

虽然不是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);
    }
}

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

.NET会自动将传递给TransformFinalBlock的数组填充为零,以使其达到块长度,但您可以通过检查inputCount(这将是实际输入长度)来检测到,如果您的算法需要填充,则替换为您自己的自定义(非零)填充。

答案 1 :(得分:2)

不,继承CryptoStream不是正确的做法。如果你想走那条路,那么正确的方法是创建一个实现ICryptoTransform的类,并将解密和加密逻辑放在那里。然后,您将ICryptoTransform类作为参数传递给CryptoStream