2009-06-02 83 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;