2

我决定在我的服务中实现文件传输加密。在此之前的文件传输没有加密,它们以完全相同的字节数完美地发送和接收。

现在我已经在混合中引入了asymmetrical加密symmetrical,以便在数据通过 TCP 协议时对其进行加密。我用来asymmetrical进行初始握手,将symmetrical密钥传递给由公钥加密的另一方asymmetric。从那时起,文件的接收者周期性地调用发送者,发送者生成一个新initialization vector的,用密钥加密数据symmetric,然后发送给接收者使用IV和相同的symmetric密钥解密。

我使用的块大小是 2mb,这样生成的块的字节大小,除了最后一个不同的块之外,是2097152. PaddingMode.PKCS7当 AES 使用和加密此文件时CipherMode.CBC,生成的字节大小为2097168. 在加密过程中它获得了大约 16 个字节。

现在最初我认为这是我的问题所在,但是当我在接收端解密数据时,它会回到2097152字节长度并将其写入文件。我已经向自己证明了它确实可以加密和解密数据。

在足够小的文件上,从原始文件到发件人的文件大小似乎完全相同。然而,当我增加更大的文件大小时,存在一个递减。在大小为 的视频文件(来自 Windows 7 安装的 Wildlife.wmv)上26,246,026 bytes,我收到的是26,246,218 bytes.

为什么会有这种大小差异?我在这里做错了什么?

这是我的一些代码。

对于我的加密,我使用以下类来加密或解密,以字节数组的形式返回结果。

public class AesCryptor
{
    public byte[] Encrypt(byte[] data, byte[] key, byte[] iv)
    {
        using (SymmetricAlgorithm aes = new AesManaged())
        {
            aes.Key = key;
            aes.IV = iv;
            aes.Padding = PaddingMode.PKCS7;
            aes.Mode = CipherMode.CBC;

            using (ICryptoTransform encryptor = aes.CreateEncryptor(key, iv))
            {
                return Crypt(data, key, iv, encryptor);
            }
        }
    }

    public byte[] Decrypt(byte[] data, byte[] key, byte[] iv)
    {
        using (SymmetricAlgorithm aes = new AesManaged())
        {
            aes.Key = key;
            aes.IV = iv;
            aes.Padding = PaddingMode.PKCS7;
            aes.Mode = CipherMode.CBC;

            using (ICryptoTransform decryptor = aes.CreateDecryptor(key, iv))
            {
                return Crypt(data, key, iv, decryptor);
            }
        }
    }

    private byte[] Crypt(byte[] data, byte[] key, byte[] iv, ICryptoTransform transform)
    {
        using (MemoryStream memoryStream = new MemoryStream())
        {
            using (CryptoStream cryptoStream = new CryptoStream(memoryStream, transform, CryptoStreamMode.Write))
            {
                cryptoStream.Write(data, 0, data.Length);
                cryptoStream.FlushFinalBlock();
            }
            return memoryStream.ToArray();
        }
    }
}

文件的发送者正在使用此代码加密数据(在私有对称密钥握手之后)(以及更多与实际加密过程无关的内容。注意 chunkedFile.NextChunk()。这调用了一个方法在为我进行文件分块的类上,返回 2mb 块大小,除非最终大小更小。

        byte[] buffer;
        byte[] iv = new byte[symmetricEncryptionBitSize / 8];
        using (var rngCrypto = new RNGCryptoServiceProvider())
            rngCrypto.GetBytes(iv);

        AesCryptor cryptor = new AesCryptor();

        buffer = cryptor.Encrypt(chunkedFile.NextChunk(), symmetricPrivateKey, iv);

下面的代码是文件的接收者使用的(不是全部,这是与数据解密有关的)。数据正在写入文件流(writer)。

                    FileMessage message = hostChannel.ReceiveFile();
                    moreChunks = message.FileMetaData.MoreChunks;

                    UpdateTotalBytesTransferred(message);

                    writer.BaseStream.Position = filePosition;

                    byte[] decryptedStream;

                    // Copy the message stream out to a memory stream so we can work on it afterwards.
                    using (var memoryStream = new MemoryStream())
                    {
                        message.ChunkData.CopyTo(memoryStream);
                        decryptedStream = cryptor.Decrypt(memoryStream.ToArray(), symmetricPrivateKey, message.FileMetaData.InitializationVector);
                    }

                    writer.Write(decryptedStream);

顺便说一句,如果需要,NextChunk 是一个非常简单的方法。

        public byte[] NextChunk()
    {
        if (MoreChunks) // If there are more chunks, procede with the next chunking operation, otherwise throw an exception.
        {
            byte[] buffer;
            using (BinaryReader reader = new BinaryReader(File.OpenRead(FilePath)))
            {
                reader.BaseStream.Position = CurrentPosition;
                buffer = reader.ReadBytes((int)MaximumChunkSize);
            }
            CurrentPosition += buffer.LongLength; // Sets the stream position to be used for the next call.

            return buffer;
        }
        else
            throw new InvalidOperationException("The last chunk of the file has already been returned.");
    }

编辑:似乎对于每个传输的块,因此每次加密,我都获得了 16 字节的文件大小。极小的文件大小不会发生这种情况。

4

1 回答 1

1

好吧,我解决了这个问题。

事实证明,我在消息数据中发送chunkLength了加密的块数据。因此,对于我发送的每个块,即使我解密并写入了正确的文件数据,我也会将流位置推进加密数据的长度。这意味着每次我解密时,当传输超过 1 个块时(这就是为什么只有 1 个块大小的小文件没有问题)我在文件大小上增加了 16 个字节。

帮助我的人可能无法弄清楚这一点,因为我没有在客户端或服务器端包含所有数据来查看这一点。但谢天谢地,我设法自己回答了这个问题。

在发件人方面,我正在像这样创建我的 FileMessage。

            FileMessage message = new FileMessage();
            message.FileMetaData = new FileMetaData(chunkedFile.MoreChunks, chunkedFile.ChunkLength, chunkedFile.CurrentPosition, iv);
            message.ChunkData = new MemoryStream(buffer);

如果你看到FileMetaData构造函数的第二个参数,我传入的chunkedFile.ChunkLength应该是块的长度。我对加密的块数据执行此操作,导致发送的块长度不正确。

另一方面,客户正在接收这些额外信息。如果你看接近尾声,你会看到代码filePosition += message.FileMetaData.ChunkLength;。我正在使用该错误chunkLength来推进文件位置。事实证明,甚至不需要设置 streamPosition。

using (BinaryWriter writer = new BinaryWriter(File.OpenWrite(fileWritePath)))
            {
                writer.BaseStream.SetLength(0);

                while (moreChunks)
                {
                    FileMessage message = hostChannel.ReceiveFile();
                    moreChunks = message.FileMetaData.MoreChunks;

                    UpdateTotalBytesTransferred(message);

                    writer.BaseStream.Position = filePosition;

                    byte[] decryptedStream;

                    // Copy the message stream out to a memory stream so we can work on it afterwards.
                    using (var memoryStream = new MemoryStream())
                    {
                        message.ChunkData.CopyTo(memoryStream);
                        Debug.WriteLine("Received Encrypted buffer Length: " + memoryStream.Length);
                        decryptedStream = cryptor.Decrypt(memoryStream.ToArray(), symmetricPrivateKey, message.FileMetaData.InitializationVector);
                        Debug.WriteLine("Received Decrypted buffer Length: " + decryptedStream.Length);
                    }

                    writer.Write(decryptedStream);

                    TotalBytesTransferred = message.FileMetaData.FilePosition;

                    filePosition += message.FileMetaData.ChunkLength;
                }

                OnTransferComplete(this, EventArgs.Empty);
                StopSession();
            }

如此简单的错误,但根本没有迅速向我扑来。

于 2013-01-16T06:26:58.557 回答