3

サーバー上のファイル (5KB のブロック単位) を読み取り、AES を使用してブロックを暗号化し、クライアントに送信しようとしています。クライアントでは、受信したブロックを復号化し、ファイルに追加して元のファイルを取得します。

ただし、クライアントで受信した復号化されたブロック サイズは、サーバーで暗号化されたプレーンテキスト ブロックとは異なります。

たとえば、15.5 KB の exe ファイルがあるので、15.5*1024/5*1024 = 4 ブロック (丸数字) を暗号化してクライアントに送信します (最初の 3 ブロックの長さは 5120 バイトで、最後のブロックの長さは 512 バイトです)。 . ただし、クライアントでは、復号化されたブロックのサイズは 5057、4970、5016、および 512 バイトであり、これは 15.1 KB のファイル サイズに相当します (サーバーによって実際に送信されたものよりも小さい)。

ここに私のコードスニペットがあります:

サーバー (ファイルをクライアントに送信):

FileStream fs = new FileStream("lcd.exe", FileMode.Open, FileAccess.Read);

        //block size = 5KB
        int blockSize = 5 * 1024;

        //calculate number of blocks in data
        long numberOfBlocks = fs.Length / blockSize;

        if (fs.Length % blockSize != 0) numberOfBlocks++;

        byte[] numberOfBlocksBytes = BitConverter.GetBytes(numberOfBlocks);

        //send number of blocks to client
        SendMessage(sw, numberOfBlocksBytes);

        int count = 0, offset = 0, numberOfBytesToRead=0;

        Aes objAes = new Aes();

        while (count < numberOfBlocks)
        {
            byte[] buffer;

            numberOfBytesToRead = blockSize;

            if (fs.Length < offset + blockSize)
            {
                numberOfBytesToRead = (int)(fs.Length - offset);

            }

                buffer = new byte[numberOfBytesToRead];

                fs.Read(buffer, 0, numberOfBytesToRead);

            //encrypt before sending
            byte[] encryptedBuffer = objAes.Encrypt(buffer, Encoding.Default.GetBytes(sessionKey), initVector);

            SendMessage(sw, encryptedBuffer);

            offset += numberOfBytesToRead;

            count++;

        }

        fs.Close();

ファイルを受け取るクライアント側のコード:

byte[] numberOfBlocksBytes = ReadMessage(sr);

        long numberOfBlocks = BitConverter.ToInt64(numberOfBlocksBytes, 0);

        FileStream fs = new FileStream("lcd.exe", FileMode.Append, FileAccess.Write);

        //block size = 5KB
        int blockSize = 5 * 1024;

        Aes objAes = new Aes();

        int count = 0, offset = 0;

        while (count < numberOfBlocks)
        {

            byte[] encryptedBuffer = ReadMessage(sr);

            byte[] buffer = objAes.Decrypt(encryptedBuffer, sessionKey, initVector);

            fs.Write(buffer, 0, buffer.Length);

            offset += buffer.Length;

            count++;

        }

        fs.Close();

暗号化のための私の AES コード:

private const int StandardKeyLength = 16;

    public byte[] Encrypt(byte[] plainText, byte[] key, byte[] initVector)
    {
        if (key.Length != StandardKeyLength | initVector.Length != StandardKeyLength)
        {
            throw new ArgumentException("Key Length and Init Vector should be 16 bytes (128 bits) in size");
        }

        var bPlainBytes = plainText;

        var objRm = new RijndaelManaged();

        objRm.Key = key;
        objRm.IV = initVector;
        objRm.Padding = PaddingMode.PKCS7;
        objRm.BlockSize = 128;

        var ict = objRm.CreateEncryptor(objRm.Key, objRm.IV);

        var objMs = new MemoryStream();
        var objCs = new CryptoStream(objMs, ict, CryptoStreamMode.Write);

        objCs.Write(bPlainBytes, 0, bPlainBytes.Length);

        objCs.FlushFinalBlock();

        var bEncrypted = objMs.ToArray();

        return bEncrypted;
    }

復号化のための私の AES コード:

    public byte[] Decrypt(byte[] cipherText, byte[] key, byte[] initVector)
    {
        if (key.Length != StandardKeyLength | initVector.Length != StandardKeyLength)
        {
            throw new ArgumentException("Key Length and Init Vector should be 16 bytes (128 bits) in size");
        }

        var bCipherBytes = cipherText;

        var objRm = new RijndaelManaged();
        objRm.Key = key;
        objRm.IV = initVector;
        objRm.Padding = PaddingMode.PKCS7;
        objRm.BlockSize = 128;

        var ict = objRm.CreateDecryptor(objRm.Key, objRm.IV);
        var objMs = new MemoryStream(bCipherBytes);
        var objCs = new CryptoStream(objMs, ict, CryptoStreamMode.Read);

        var streamobj = new StreamReader(objCs);

        var strDecrypted = streamobj.ReadToEnd();

        return (Encoding.Default.GetBytes(strDecrypted));
    }

これらは、サーバー上でファイル ブロックを送信する while ループをデバッグしているときに得た結果です。

送信された実際のファイル サイズ: 15.5 KB = 15872 バイト

バッファサイズ(平文) 暗号化バッファサイズ(送信) オフセット数

5120 5136 5120 0

5120 5136 10240 1

5120 5136 15360 2

 512 528 15872 3

これらは、クライアントでファイル ブロックを受け取る while ループをデバッグしているときに得た結果です。

受信した実際のファイル サイズ: 15.1 KB = 15555 バイト

受信バッファサイズ 復号バッファサイズ オフセット数

5136 5057 5057 0

5136 4970 10027 1

5136 5016 15043 2

 528 512 15555 3

送信コードと受信コードが正常に動作していることは明らかです (送信される暗号化されたバッファー サイズ = 受信バッファー サイズであるため)。ただし、復号化されたバッファ サイズは、長さが 512 バイトの最後のブロックを除いて、バッファ サイズ (平文) とまったく一致しません。

クライアント側でファイルを完全に受信していないため、復号化の何が問題になる可能性がありますか?

4

3 に答える 3

2

Decrypt ステートメントで暗号文を文字列であるかのように扱っているため、つまずいています。具体的には、次の行:

var streamobj = new StreamReader(objCs);
var strDecrypted = streamobj.ReadToEnd();

return (Encoding.Default.GetBytes(strDecrypted));

代わりに、Raw バイト配列をバッファーに読み込むために、CryptoStream で Read を呼び出す必要があります。その後、そのバッファを文字列に強制することなく返すことができます (これは、ストリーム リーダーを使用して行われていることです)。

次のようなものを使用する必要があります。

public byte[] Decrypt(byte[] cipherText, byte[] key, byte[] initVector)
{
    if (key.Length != StandardKeyLength | initVector.Length != StandardKeyLength)
    {
        throw new ArgumentException("Key Length and Init Vector should be 16 bytes (128 bits) in size");
    }

    var bCipherBytes = cipherText;

    var objRm = new RijndaelManaged();
    objRm.Key = key;
    objRm.IV = initVector;
    objRm.Padding = PaddingMode.PKCS7;
    objRm.BlockSize = 128;

    var ict = objRm.CreateDecryptor(objRm.Key, objRm.IV);
    var objMs = new MemoryStream(bCipherBytes);
    var objCs = new CryptoStream(objMs, ict, CryptoStreamMode.Read);

    var buffer = new byte[cipherText.Length];
    int readBytes = objCs.Read(buffer, 0, cipherText.Length);

    var trimmedData = new byte[readBytes];
    Array.Copy(buffer, trimmedData, readBytes);
    return trimmedData;
}

また、私が管理している Snipt の暗号化ユーティリティもご覧になることをお勧めします。具体的には、対称暗号化と復号化の方法です。現状のコードには、多くの使用ブロックがあり、多くの潜在的なリソース リークがあります。

于 2013-07-05T14:02:29.740 に答える
-1

私の無知かもしれない簡単な観察: Encrypt() メソッドは、デフォルトのエンコーディングを使用してセッションキーバイトを取得します。受信側では、Decrypt() メソッドは、sessionKey 自体を 2 番目のパラメータとして使用します。つまり、バイトを取得せずに?

于 2013-07-05T13:38:04.273 に答える