【问题标题】:Rijndael algorithm goes wrongRijndael 算法出错
【发布时间】:2017-08-28 09:06:42
【问题描述】:

我找到了这篇文章:link to msdn

我正在尝试做的事情:加密 Byte() 数组,然后解密它。 它可以工作,但解密的结果不等于原始数组。 我的代码:

Dim RMCrypto As New RijndaelManaged()
RMCrypto.Key = Key
RMCrypto.IV = IV
RMCrypto.Padding = PaddingMode.Zeros

Dim dataToDecrypt As Byte() = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}

Dim encrypted As Byte() = Encrypt(dataToDecrypt, RMCrypto)
Dim roundtrip As Byte() = Decrypt(encrypted, RMCrypto)

在哪里

Private Function Encrypt(ByVal plainText As Byte(), RMCrypto As RijndaelManaged) As Byte()

Dim encrypted() As Byte
Using RMCrypto
    ' Create a decrytor to perform the stream transform.
    Dim encryptor As ICryptoTransform = RMCrypto.CreateEncryptor(RMCrypto.Key, RMCrypto.IV)
    ' Create the streams used for encryption.
    Using msEncrypt As New MemoryStream()
        Using csEncrypt As New CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write)
            Using swEncrypt As New StreamWriter(csEncrypt)

                'Write all data to the stream.
                swEncrypt.Write(plainText)
            End Using
            encrypted = msEncrypt.ToArray()
        End Using
    End Using
End Using

    ' Return the encrypted bytes from the memory stream.
    Return encrypted

End Function 'Encrypt

Private Function Decrypt(ByVal cipherText() As Byte, RMCrypto As RijndaelManaged) As Byte()
Dim plaintext As Byte()

Using RMCrypto 

' Create a decrytor to perform the stream transform.
Dim decryptor As ICryptoTransform = RMCrypto.CreateDecryptor(RMCrypto.Key, RMCrypto.IV)

' Create the streams used for decryption.
    Using msDecrypt As New MemoryStream(cipherText)
        Using csDecrypt As New CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read)
            Using srDecrypt As New StreamReader(csDecrypt)
                ' Read the decrypted bytes from the decrypting stream
                ' and place them in a string.
                plaintext = Encoding.UTF8.GetBytes(srDecrypt.ReadToEnd())
            End Using
        End Using
    End Using
End Using

Return plaintext

End Function 'Decrypt

它是如何工作的:

Input: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
encrypted: 221 54 108 65 95 233 31 124 101 181 205 176 13 233 85 252
decrypted: 15 45 15 239 191 189 239 191 189 14 58 239 191 189 9 118 37 239 191 189 212 149 239 191 189 58

如我们所见,看起来加密是对的,但解密是完全错误的——它甚至还有更多的成员!

附:可能是由于Decrypt 函数中从字符串到 Byte() 的错误转换吗? 更有趣的时刻:decrypted改变它的大小,每次都给出一个新的答案!

【问题讨论】:

  • Rijndael 是一种分组密码,在默认 (CBC) 模式下,它以 128 位 = 16 字节的块生成/预期密文。您的 dataToDecrypt 仅 11 个字节长,因此不是一个完整的块,因此无法解密。
  • @Iridium 你是对的!我将它的大小更改为 16 字节并得到另一个错误。你能看看这个吗?
  • 尝试更改rijAlg.Padding的不同值,它可以解决Iridium描述的问题。
  • 在从内存流中读取内容之前,需要让 CryptoStream 的使用结束。
  • encrypted = msEncrypt.ToArray() 在加密期间应该在CryptoStreamEnd Using 后面。解密期间的读取可能没问题。但是不要在那里转换为 UTF-8,你只需要返回字节。

标签: .net vb.net encryption stream cryptography


【解决方案1】:

好的,你的代码确实有很多问题。

我的代码 sn-ps 将使用 C#,因为我已经 15 年没有编写 VB 了。虽然他们可以类似地阅读,但我不想最终给出无效的语法。

1) 您通过using 处理您的对象,然后再次使用它。

显然,对于RijndaelManaged,这只是将其重新置于“我从未使用过的状态”,这会导致下一次使用生成随机密钥和 IV。这可能是你最大的问题。

因此,您的 Encrypt 和 Decrypt 方法应删除对称算法对象(当前名为 RMCrypto)的 using 语句。通常对象的创建者拥有生命周期,有时生命周期被移交给新对象;但几乎从不应该由您调用的方法终止生命周期。

2) 您正在使用 RijndaelManaged。不要。

您需要 AES(Rijndael 的一种受限形式,但互操作性要好得多)。你“可以”切换到AesManaged,但不应该。相反,您应该使用 Aes.Create()(某些 Aes 派生类型仅适用于特定(特定版本)操作系统。Aes.Create() 应该始终有效。

3) 您在 CryptoStream 完成之前从 MemoryStream 中读取数据。

您对msEncrypt.ToArray() 的调用位于 CryptoStream 的using 内,但您希望它位于外部。翻转这两行。

4) 您正在使用 PaddingMode.Zeros。

虽然这可能是您想要的,但可能不是。 AES(和 Rijndael)是一种分组密码算法,分组密码需要完整的块才能运行。当最后一个块不足时,它会完成“padded”。 PKCS7是“标准”的填充模式,解密可以去掉(因为它有足够强的规则)。 ANSI X.923 和 ISO 10126 也可以在解密过程中被删除。零不能,因为“unpadder”无法判断零是实零还是填充零。

5) 您将变量命名为 dataToDecrypt

您将其传递给 Encrypt,然后将 Encrypt 的输出传递给 Decrypt。所以这只是data

6) 您在 Decrypt 中应用了 UTF-8 解码。

您的文本是从 1 到 16 的字节,这绝对不是 UTF-8 文本。

您使用的 StreamReader/StreamWriter 表明您正在尝试对字符串执行加密。别。加密/解密是基于字节的。 (除了像凯撒密码这样的新奇事物)。

总而言之:

// The using goes here, with the Create call. Not in anything it gets passed to.
using (Aes aes = Aes.Create())
{
    aes.Key = key;
    aes.IV = iv;
    // PKCS7 is the default padding mode,
    // if you don't like trusting defaults you can set it here.

    byte[] data = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 };

    byte[] encrypted = Encrypt(data, aes);
    byte[] roundtrip = Decrypt(encrypted, aes);
}

private static byte[] Encrypt(byte[] data, SymmetricAlgorithm alg)
{
    // You didn't have a using for this, but should have:
    using (ICryptoTransform encryptor = alg.CreateEncryptor())
    using (MemoryStream msEncrypt = new MemoryStream())
    {
        using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
        {
            csEncrypt.Write(data, 0, data.Length);
        }

        // Now that the CryptoStream has closed the data has been padded.
        return msEncrypt.ToArray();
    }
}

private static byte[] Decrypt(byte[] data, SymmetricAlgorithm alg)
{
    using (ICryptoTransform decryptor = alg.CreateDecryptor())
    using (MemoryStream msDecrypt = new MemoryStream())
    {
        // Just keep using this in write mode. All it does is say whether
        // You'll be explicitly loading data via Write, or implicitly via Read.
        using (CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Write))
        {
            csDecrypt.Write(data, 0, data.Length);
        }

        // Now that the CryptoStream has closed the data has been de-padded.
        return msDecrypt.ToArray();
    }
}

【讨论】:

  • Bartonjs,这个答案太棒了!非常感谢,我得到了很多信息!现在using 问题对我来说很清楚了。我会尝试aes 而不是Rijndael,但重要的是在 3d 方应用程序中使用什么方法加密数据(正如我告诉我的任务只是解密数据)。我对填充进行了很多思考,我知道密码的块结构,据我所知,他们对此使用零填充,并为数据中有用的字节数使用附加数字。关于获取字节而不是字符串的很好的说明-我的代码基于我在网络中找到的示例,所以我使用了它:(
  • 还有一条评论:我想这是您的代码中的一个错字:在解密函数中必须是 using (ICryptoTransform decryptor = alg.CreateDecryptor()) 而不是 using (ICryptoTransform encryptor = alg.CreateEncryptor())
  • Bartonjs,我在 VB.net 中重写,完美运行!谢谢你。我还有一个关于算法的问题:当加密
  • 如果你有一个“正确的答案”,那么你可能想要 PaddingMode.Zeros。如果这没有帮助,或者您想了解更多信息,那最好作为一个不同的问题(可能已经存在,但我找不到它)。
  • 是的! PaddingMode.Zeros 完美运行!最后一个问题:我一步步解密了很多数据。在全局中声明Aes而不是每次获取数据时都创建它会更有效吗?
猜你喜欢
  • 2014-04-16
  • 1970-01-01
  • 1970-01-01
  • 2016-10-21
  • 2015-03-11
  • 2014-06-09
  • 2011-02-09
  • 1970-01-01
  • 2014-07-07
相关资源
最近更新 更多