2

私のサービスでは、ファイル転送に暗号化を実装することにしました。これ以前のファイル転送は暗号化されておらず、まったく同じバイト数で問題なく送受信されていました。

これで、TCPプロトコルを通過するときにデータを暗号化するための暗号化をミックスに導入asymmetricalしました。symmetrical私は、公開鍵で暗号化された相手asymmetricalに鍵を渡す最初のハンドシェイクを行うために使用します。それ以降、ファイルの受信者は定期的に送信者に電話をかけ、送信者は新しいを生成し、キーでデータを暗号化し、IVと同じキーを使用して受信者が復号化するために送信します。symmetricalasymmetricinitialization vectorsymmetricsymmetric

私が使用しているチャンクサイズは2mbであるため、生成されたチャンクのバイトサイズは、変化する最後のチャンクを除いて、です2097152。AESがこのファイルをPaddingMode.PKCS7とで暗号化すると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);

以下のコードは、ファイルの受信者が使用するものです(すべてではありません。これは、データの復号化に関係するものです)。データはファイルstream(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 つのチャンク サイズのみの小さなファイルに問題がなかった理由です)、ファイル サイズに 16 バイトを追加していたことを意味します。

これを見るためにクライアント側またはサーバー側にすべてのデータを含めなかったので、私を助けてくれた人はおそらくこれを理解できなかったでしょう. しかし、ありがたいことに、私は自分でそれに答えることができました。

送信者側では、このように FileMessage を作成していました。

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

コンストラクターの 2 番目のパラメーターが表示されている場合は、チャンクの長さとなるはずの値を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 に答える