2009-06-02 80 views
11

我試圖加密和解密使用上一個RijndaelManaged的套接字文件流,但我一直碰到異常長度無效

CryptographicException: Length of the data to decrypt is invalid. 
    at System.Security.Cryptography.RijndaelManagedTransform.TransformFinalBlock(Byte[] inputBuffer, Int32 inputOffset, Int32 inputCount) 
    at System.Security.Cryptography.CryptoStream.FlushFinalBlock() 
    at System.Security.Cryptography.CryptoStream.Dispose(Boolean disposing) 

唯一的例外是在年底拋出receiveFile中的使用語句,當整個文件已被傳輸時。

我試過在網上搜索,但只找到了加密和解密單個字符串時使用編碼時出現的問題的答案。我使用FileStream,所以我沒有指定任何使用的編碼,所以這不應該是問題。這些是我的方法:

private void transferFile(FileInfo file, long position, long readBytes) 
{ 
    // transfer on socket stream 
    Stream stream = new FileStream(file.FullName, FileMode.Open); 
    if (position > 0) 
    { 
     stream.Seek(position, SeekOrigin.Begin); 
    } 
    // if this should be encrypted, wrap the encryptor stream 
    if (UseCipher) 
    { 
     stream = new CryptoStream(stream, streamEncryptor, CryptoStreamMode.Read); 
    } 
    using (stream) 
    { 
     int read; 
     byte[] array = new byte[8096]; 
     while ((read = stream.Read(array, 0, array.Length)) > 0) 
     { 
      streamSocket.Send(array, 0, read, SocketFlags.None); 
      position += read; 
     } 
    } 
} 

private void receiveFile(FileInfo transferFile) 
{ 
    byte[] array = new byte[8096]; 
    // receive file 
    Stream stream = new FileStream(transferFile.FullName, FileMode.Append); 
    if (UseCipher) 
    { 
     stream = new CryptoStream(stream, streamDecryptor, CryptoStreamMode.Write); 
    } 
    using (stream) 
    { 
     long position = new FileInfo(transferFile.Path).Length; 
     while (position < transferFile.Length) 
     { 
      int maxRead = Math.Min(array.Length, (int)(transferFile.Length - position)); 
      int read = position < array.Length 
         ? streamSocket.Receive(array, maxRead, SocketFlags.None) 
         : streamSocket.Receive(array, SocketFlags.None); 
      stream.Write(array, 0, read); 
      position += read; 
     } 
    } 
} 

這是我用來設置密碼的方法。 byte [] init是一個生成的字節數組。

private void setupStreamCipher(byte[] init) 
{ 
    RijndaelManaged cipher = new RijndaelManaged(); 
    cipher.KeySize = cipher.BlockSize = 256; // bit size 
    cipher.Mode = CipherMode.ECB; 
    cipher.Padding = PaddingMode.ISO10126; 
    byte[] keyBytes = new byte[32]; 
    byte[] ivBytes = new byte[32]; 

    Array.Copy(init, keyBytes, 32); 
    Array.Copy(init, 32, ivBytes, 0, 32); 

    streamEncryptor = cipher.CreateEncryptor(keyBytes, ivBytes); 
    streamDecryptor = cipher.CreateDecryptor(keyBytes, ivBytes); 
} 

任何人有一個想法,我可能會做錯什麼?

回答

6

它看起來像我不喜歡發送最後的塊。您至少需要FlushFinalBlock()發送CryptoStream以確保發送最終的塊(接收流正在查找)。

順便說一下,CipherMode.ECB is more than likely an epic fail就您所做的安全而言。至少使用CipherMode.CBC(密碼塊鏈接)實際上使用IV,並使每個塊依賴於前一個。

編輯:哎呀,加密流處於讀取模式。在這種情況下,您需要確保您讀取EOF,以便CryptoStream可以處理最終塊,而不是在readBytes之後停止。如果您在寫入模式下運行加密流,則可能會更容易控制。

還有一點需要注意:你不能假定字節等於字節。分組密碼具有它們處理的固定塊大小,除非您使用將分組密碼轉換爲流密碼的密碼模式,否則會有填充使密文長於明文。

+1

的FlushFinalBlock()方法被調用在using語句

using(stream) { // } // calls Close() -> FlushFinalBlock()
我將改變CipherMode的「關閉部分」,我剛剛進入它作爲一個例子,所以你知道,我不初始化我的密碼在任何「怪異」的方式。 sendFile()中的readBytes尚未使用,我忘記刪除它。我讀到文件末尾,所以這不應該成爲問題。 我以爲
cipher.Padding = PaddingMode.ISO10126;
正在照顧填充?我可以改變什麼來使它工作? – Patrick 2009-06-03 12:02:26

+0

如果加密流處於讀取模式,那麼如果您放棄它,最後的塊將會丟失;它必須從其基礎源流中實際讀取文件結尾以產生最終塊。 – 2009-06-03 16:43:55

0
cipher.Mode = CipherMode.ECB; 

唉!滾動你自己的安全代碼幾乎總是一個壞主意。

1

傑弗裏Hantin提出的意見後,我改變了receiveFile一些行

using (stream) { 
    FileInfo finfo = new FileInfo(transferFile.Path); 
    long position = finfo.Length; 
    while (position < transferFile.Length) { 
     int maxRead = Math.Min(array.Length, (int)(transferFile.Length - position)); 
     int read = position < array.Length 
        ? streamSocket.Receive(array, maxRead, SocketFlags.None) 
        : streamSocket.Receive(array, SocketFlags.None); 
     stream.Write(array, 0, read); 
     position += read; 
    } 
} 

->

using (stream) { 
    int read = array.Length; 
    while ((read = streamSocket.Receive(array, read, SocketFlags.None)) > 0) { 
     stream.Write(array, 0, read); 
     if ((read = streamSocket.Available) == 0) { 
      break; 
     } 
    } 
} 

瞧,她的作品(因爲我沒有了以往任何時候都這麼樣的填充之前並不在意)。如果Available返回0,即使所有數據都沒有被傳輸,我也不確定會發生什麼,但在那種情況下我會傾向於此。感謝您的幫助傑弗裏!

問候。

0

我的我只是刪除了填充和它的作品

評論了這一點 - cipher.Padding = PaddingMode。ISO10126;