要解密的数据长度无效

时间:2021-12-20 15:24:54

I'm trying to encrypt and decrypt a file stream over a socket using RijndaelManaged, but I keep bumping into the exception

我正在尝试使用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)

The exception is thrown at the end of the using statement in receiveFile, when the whole file has been transferred.

当整个文件被传输时,在receiveFile中的using语句结束时抛出异常。

I tried searching the web but only found answers to problems that arise when using Encoding when encrypting and decrypting a single string. I use a FileStream, so I don't specify any Encoding to be used, so that should not be the problem. These are my methods:

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

This is the method I use to set up the ciphers. byte[] init is a generated byte array.

这是我用来设置密码的方法。 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);
}

Anyone have an idea in what I might be doing wrong?

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

4 个解决方案

#1


It looks to me like you're not properly sending the final block. You need to at least FlushFinalBlock() the sending CryptoStream in order to ensure that the final block (which the receiving stream is looking for) is sent.

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

By the way, CipherMode.ECB is more than likely an epic fail in terms of security for what you're doing. At least use CipherMode.CBC (cipher-block chaining) which actually uses the IV and makes each block dependent on the previous one.

顺便说一句,CipherMode.ECB很可能是你正在做的安全方面的史诗般的失败。至少使用CipherMode.CBC(密码块链接),它实际上使用IV并使每个块依赖于前一个。

EDIT: Whoops, the enciphering stream is in read mode. In that case you need to make sure you read to EOF so that the CryptoStream can deal with the final block, rather than stopping after readBytes. It's probably easier to control if you run the enciphering stream in write mode.

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

One more note: You cannot assume that bytes in equals bytes out. Block ciphers have a fixed block size they process, and unless you are using a cipher mode that converts the block cipher to a stream cipher, there will be padding that makes the ciphertext longer than the plaintext.

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

#2


After the comment made by Jeffrey Hantin, I changed some lines in receiveFile to

在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;
        }
    }
}

And voila, she works (because of the ever so kind padding that I didn't care to bother about earlier). I'm not sure what happens if Available returns 0 even though all data hasn't been transferred, but I'll tend to that later in that case. Thanks for your help Jeffrey!

瞧,她的工作(因为我之前并不在乎打扰的那种填充物)。即使所有数据都没有被传输,我也不确定如果Available返回0会发生什么,但在那种情况下我会倾向于那样。谢谢你的帮助杰弗里!

Regards.

#3


cipher.Mode = CipherMode.ECB;

Argh! Rolling your own security code is almost always a bad idea.

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

#4


Mine i just removed the padding and it works

我只是删除了填充,它的工作原理

Commented this out - cipher.Padding = PaddingMode.ISO10126;

评论出来了 - cipher.Padding = PaddingMode.ISO10126;

#1


It looks to me like you're not properly sending the final block. You need to at least FlushFinalBlock() the sending CryptoStream in order to ensure that the final block (which the receiving stream is looking for) is sent.

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

By the way, CipherMode.ECB is more than likely an epic fail in terms of security for what you're doing. At least use CipherMode.CBC (cipher-block chaining) which actually uses the IV and makes each block dependent on the previous one.

顺便说一句,CipherMode.ECB很可能是你正在做的安全方面的史诗般的失败。至少使用CipherMode.CBC(密码块链接),它实际上使用IV并使每个块依赖于前一个。

EDIT: Whoops, the enciphering stream is in read mode. In that case you need to make sure you read to EOF so that the CryptoStream can deal with the final block, rather than stopping after readBytes. It's probably easier to control if you run the enciphering stream in write mode.

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

One more note: You cannot assume that bytes in equals bytes out. Block ciphers have a fixed block size they process, and unless you are using a cipher mode that converts the block cipher to a stream cipher, there will be padding that makes the ciphertext longer than the plaintext.

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

#2


After the comment made by Jeffrey Hantin, I changed some lines in receiveFile to

在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;
        }
    }
}

And voila, she works (because of the ever so kind padding that I didn't care to bother about earlier). I'm not sure what happens if Available returns 0 even though all data hasn't been transferred, but I'll tend to that later in that case. Thanks for your help Jeffrey!

瞧,她的工作(因为我之前并不在乎打扰的那种填充物)。即使所有数据都没有被传输,我也不确定如果Available返回0会发生什么,但在那种情况下我会倾向于那样。谢谢你的帮助杰弗里!

Regards.

#3


cipher.Mode = CipherMode.ECB;

Argh! Rolling your own security code is almost always a bad idea.

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

#4


Mine i just removed the padding and it works

我只是删除了填充,它的工作原理

Commented this out - cipher.Padding = PaddingMode.ISO10126;

评论出来了 - cipher.Padding = PaddingMode.ISO10126;