Wcf AES加密和解密导致文件与原始文件不同

Wcf AES加密和解密导致文件与原始文件不同,wcf,encryption,aes,Wcf,Encryption,Aes,我决定在我的服务中对文件传输实施加密。在此之前的文件传输是不加密的,发送和接收的字节数完全相同 现在,我在混合中引入了非对称和对称加密,以在数据通过TCP协议时对其进行加密。我使用symmetric进行初始握手,将symmetric密钥传递给由asmmetric公钥加密的另一方。从那时起,文件的接收者定期调用发送者,发送者生成一个新的初始化向量,用对称密钥加密数据,并将其发送给接收者,由接收者使用IV和相同的对称密钥解密 我使用的块大小是2mb,因此生成的块的字节大小是2097152,但最后一个

我决定在我的服务中对文件传输实施加密。在此之前的文件传输是不加密的,发送和接收的字节数完全相同

现在,我在混合中引入了
非对称
对称
加密,以在数据通过TCP协议时对其进行加密。我使用
symmetric
进行初始握手,将
symmetric
密钥传递给由
asmmetric
公钥加密的另一方。从那时起,文件的接收者定期调用发送者,发送者生成一个新的
初始化向量
,用
对称
密钥加密数据,并将其发送给接收者,由接收者使用IV和相同的
对称
密钥解密

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

起初我认为这就是我的问题所在,但当我在接收端解密数据时,它返回到
2097152
字节长度,然后我将其写入文件。我已经向自己证明,它确实对数据进行加密和解密

在足够小的文件上,从原始文件到发送者的文件大小似乎完全相同。但是,当我逐步增大文件大小时,会出现一种下降趋势。在一个大小为
26246026字节
的视频文件(windows 7安装中的wivelet.wmv)上,我收到的是一个完成的传输,该传输的大小为
26246218字节

为什么会有这种尺寸差异?我做错了什么

这是我的一些代码

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

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

顺便说一下,如果需要,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字节的文件大小。对于非常小的文件大小,这不会发生。

我解决了这个问题

事实证明,我在消息数据中发送了加密区块数据的
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();
            }

这是一个简单的错误,但它并没有迅速向我扑来。

这并不能解决您的密码问题,但您可以配置WCF来执行消息和传输(https)加密。Matthew-是的,但不幸的是,我使用了分块方法并将这些分块流式传输,因此我无法使用加密或可靠的消息传递。我必须使用自己的代码来实现这一点,并且不能依赖WCF。我发现流式传输比缓冲消息传输快得多,所以这就是我选择此路径的原因.
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();
            }