首页 文章

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

提问于
浏览
2

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

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

我正在使用的块大小是2mb,这样生成的块的字节大小(除了最后一个变化的块)是 2097152 . 当AES使用 PaddingMode.PKCS7CipherMode.CBC 加密此文件时,生成的字节大小为 2097168 . 它在加密过程中获得了大约16个字节 .

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

在一个足够小的文件上,从原始文件到发送者的文件大小似乎完全相同 . 但是,当我升级到更大的文件大小时,存在一种不确定性 . 在大小为 26,246,026 bytes 的视频文件(来自Windows 7安装的Wildlife.wmv)上,我收到了 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.");
    }

EDIT: 似乎对于每个传输的块,因此每次加密,我的文件大小增加了16bytes . 对于极小的文件大小,这不会发生 .

1 回答

  • 1

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

    事实证明,我在消息数据中发送了加密块数据的 chunkLength . 因此,对于我发送的每个块,即使我解密并编写了正确的filedata,我也按照加密数据的长度推进了流的位置 . 这意味着每次我解密时,当传输超过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();
                }
    

    Such a simple bug, but one that wasn't leaping out at me quickly at all.

相关问题