C# 要解密的数据长度无效

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/942139/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-06 03:52:17  来源:igfitidea点击:

Length of the data to decrypt is invalid

c#cryptographyrijndaelmanaged

提问by Patrick

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?

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

采纳答案by Jeffrey Hantin

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

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

By the way, CipherMode.ECBis more than likely an epic failin 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(cipher-block chaining) 实际上使用 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.

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

回答by Alun Harford

cipher.Mode = CipherMode.ECB;

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

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

回答by Patrick

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.

问候。

回答by jayson.centeno

Mine i just removed the padding and it works

我的我刚刚删除了填充,它可以工作

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

注释掉这个 - cipher.Padding = PaddingMode.ISO10126;