要解密的数据长度无效

时间:2009-06-02 22:07:56

标签: c# cryptography rijndaelmanaged

我正在尝试使用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中的using语句结束时抛出异常。

我尝试在网上搜索,但只找到了在加密和解密单个字符串时使用编码时出现的问题的答案。我使用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);
}

任何人都知道我可能做错了什么?

4 个答案:

答案 0 :(得分:6)

在我看来,你没有正确发送最后一个块。您需要至少FlushFinalBlock()发送CryptoStream,以确保发送最终块(接收流正在寻找)。

顺便说一下,CipherMode.ECB is more than likely an epic fail就你正在做的事情的安全性而言。至少使用CipherMode.CBC(密码块链接)实际使用IV并使每个块依赖于前一个。

编辑:哎呀,加密流处于读取模式。在这种情况下,您需要确保读取EOF以便CryptoStream可以处理最终块,而不是在readBytes之后停止。如果在写入模式下运行加密流,则可能更容易控制。

还有一点需要注意:你不能假设字节数等于字节数。分组密码具有固定的块大小,除非您使用将分组密码转换为流密码的密码模式,否则将使用填充使密文长于明文。

答案 1 :(得分:1)

在Jeffrey 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会发生什么,但在那种情况下我会倾向于那个。谢谢你的帮助杰弗里!

问候。

答案 2 :(得分:0)

cipher.Mode = CipherMode.ECB;

哎呀!滚动自己的安全代码几乎总是一个坏主意。

答案 3 :(得分:0)

我只是删除了填充,它可以正常工作

对此进行评论 - cipher.Padding = PaddingMode.ISO10126;