C# 为什么 RijndaelManaged 和 AesCryptoServiceProvider 返回不同的结果?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/957388/
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 04:14:49  来源:igfitidea点击:

Why are RijndaelManaged and AesCryptoServiceProvider returning different results?

c#.netencryptionrijndaelmanagedaescryptoserviceprovider

提问by SwDevMan81

Here is the example that I have run. It has the same Mode, Padding, BlockSize, KeySize. I am using the same init vector, key and data.

这是我运行的示例。它具有相同的 Mode、Padding、BlockSize、KeySize。我使用相同的初始化向量、键和数据。

Using the RijndaelManaged produces an encrypted value of: 0x8d,0x81,0x27,0xc6,0x3c,0xe2,0x53,0x2f,0x35,0x78,0x90,0xc2,0x2e,0x3b,0x8a,0x61, 0x41,0x47,0xd6,0xd0,0xff,0x92,0x72,0x3d,0xc6,0x16,0x2b,0xd8,0xb5,0xd9,0x12,0x85

使用 RijndaelManaged 会产生一个加密值:0x8d,0x81,0x27,0xc6,0x3c,0xe2,0x53,0x2f,0x35,0x78,0x90,0xc2,0x2e,0x3b,0x8a,0x61,x0x0x4,0x4,0 ,0x92,0x72,0x3d,0xc6,0x16,0x2b,0xd8,0xb5,0xd9,0x12,0x85

Using the AesCryptoServiceProvider produces an encrypted value of: 0x8d,0x9f,0x6e,0x99,0xe9,0x54,0x8b,0x12,0xa9,0x88,0x1a,0x3d,0x65,0x23,0x9c,0x4e, 0x18,0x5a,0x89,0x31,0xf5,0x75,0xc5,0x9e,0x0d,0x43,0xe9,0x86,0xd4,0xf3,0x64,0x3a

使用 AesCryptoServiceProvider 会产生一个加密值: 0x8d,0x9f,0x6e,0x99,0xe9,0x54,0x8b,0x12,0xa9,0x88,0x1a,0x3d,0x65,0x23,0x9c,0x4e,x01,x50 ,0x75,0xc5,0x9e,0x0d,0x43,0xe9,0x86,0xd4,0xf3,0x64,0x3a

Here is the code I used to generate these results

这是我用来生成这些结果的代码


   public partial class AesTest
   {
      private SymmetricAlgorithm mEncryptionType;
      private byte[] mPrivateKey;
      private byte[] mInitializationVector;
      private byte[] mData;

      public AesTest()
      {
         mPrivateKey = new byte[32] 
         { 
            0x22, 0x22, 0x22, 0x22, 
            0x22, 0x22, 0x22, 0x22, 
            0x22, 0x22, 0x22, 0x22, 
            0x22, 0x22, 0x22, 0x22,
            0x22, 0x22, 0x22, 0x22, 
            0x22, 0x22, 0x22, 0x22, 
            0x22, 0x22, 0x22, 0x22, 
            0x22, 0x22, 0x22, 0x22
         };

         mInitializationVector = new byte[16]
         { 
            0x33, 0x33, 0x33, 0x33,
            0x33, 0x33, 0x33, 0x33,
            0x33, 0x33, 0x33, 0x33,
            0x33, 0x33, 0x33, 0x33
         };

         mData = new byte[16]
         {
            0x44, 0x44, 0x44, 0x44,
            0x44, 0x44, 0x44, 0x44,
            0x44, 0x44, 0x44, 0x44,
            0x44, 0x44, 0x44, 0x44
         };

         mEncryptionType = new RijndaelManaged();
         mEncryptionType.Mode = CipherMode.CFB;
         mEncryptionType.Padding = PaddingMode.PKCS7;
         mEncryptionType.BlockSize = 128;
         mEncryptionType.KeySize = 256;

         byte[] rij_encrypted_data = Encrypt(mData);

         mEncryptionType = new AesCryptoServiceProvider();
         mEncryptionType.Mode = CipherMode.CFB;
         mEncryptionType.Padding = PaddingMode.PKCS7;
         mEncryptionType.BlockSize = 128;
         mEncryptionType.KeySize = 256;

         byte[] aes_encrypted_data = Encrypt(mData);
      }

      public virtual byte[] Encrypt(byte[] unencryptedData)
      {
         return TransformData(unencryptedData, mEncryptionType.CreateEncryptor(mPrivateKey, mInitializationVector));
      }

      private byte[] TransformData(byte[] dataToTransform, ICryptoTransform cryptoTransform)
      {
         byte[] result = new byte[0];
         if (dataToTransform != null && cryptoTransform != null && dataToTransform.Length > 0)
         {
            // Create the memory stream to store the results
            MemoryStream mem_stream = new MemoryStream();
            // Create the crypto stream to do the transformation
            CryptoStream crypto_stream = new CryptoStream(mem_stream, cryptoTransform, CryptoStreamMode.Write);
            // bytes are transformed on a write
            crypto_stream.Write(dataToTransform, 0, dataToTransform.Length);
            // Flush the final block
            crypto_stream.FlushFinalBlock();
            // Convert the transformed memory stream back to a byte array
            result = mem_stream.ToArray();
            // Close the streams
            mem_stream.Close();
            crypto_stream.Close();
         }
         return result;
      }
   }

I guess I'm just wondering if I missed something.

我想我只是想知道我是否错过了什么。

Update: Turns out that AesManagedwill throw a CryptographicException ("The specified cipher mode is not valid for this algorithm") if you try and set the CipherMode to CFB. I feel that the AesCryptoServiceProvidershould do that same, but it doesnt. Seems funny that the FIPS Certified class allows invalid cipher modes.

更新:事实证明,如果您尝试将 CipherMode 设置为 CFB ,AesManaged将抛出 CryptographicException(“指定的密码模式对该算法无效”)。我觉得AesCryptoServiceProvider 也应该这样做,但事实并非如此。FIPS 认证类允许无效的密码模式似乎很有趣。

采纳答案by goodguys_activate

Response from Microsoft:

来自微软的回应:

RijndaelManagedclass and AesCryptoServiceProviderclass are two different implementations. RijndaelManagedclass is a kind of implementation of Rijndael algorithm in .net framework, which was not validated under NIST (National Institute of Standards and Technology) Cryptographic Module Validation Program (CMVP).

RijndaelManagedclass 和 AesCryptoServiceProviderclass 是两种不同的实现。 RijndaelManagedclass 是 .net 框架中 Rijndael 算法的一种实现,未在 NIST(美国国家标准与技术研究院)密码模块验证计划 (CMVP) 下进行验证。

However, AesCryptoServiceProviderclass calls the Windows Crypto API, which uses RSAENH.DLL, and has been validated by NIST in CMVP. Although Rijndael algorithm was the winner of the NIST competition to select the algorithm that would become AES, there are some differences between Rijndael and official AES. Therefore, RijndaelManaged class and AesCryptoServiceProviderclass have subtle differences on implementation.

但是, AesCryptoServiceProvider类调用 Windows Crypto API,它使用 RSAENH.DLL,并已在 CMVP 中由 NIST 验证。尽管 Rijndael 算法是 NIST 选择成为 AES 的算法的竞赛的获胜者,但 Rijndael 和官方 AES 之间存在一些差异。因此,RijndaelManaged 类和 AesCryptoServiceProvider类在实现上存在细微差别。

In addition, RijndaelManagedclass cannot provide an equivalent implementation with AES. There is another class implemented in .net framework, AesManagedclass. This class just wrapped RijndaelManagedclass with a fixed block size and iteration count to achieve the AES standard. However, it does not support the feedback size, especially, when the mode is set as CFB or OFB, the CryptographicExceptionwill be thrown.

此外,RijndaelManaged类不能提供与 AES 等效的实现。在 .net 框架中实现了另一个类,AesManagedclass。这个类只是用RijndaelManaged固定的块大小和迭代次数包装类来实现 AES 标准。但是,它不支持反馈大小,特别是当模式设置为 CFB 或 OFB 时, CryptographicException将被抛出。

For more information, please refer to the following MSDN documents.

有关更多信息,请参阅以下 MSDN 文档。

AesManaged Classand AesManaged.Mode Property

AesManaged 类AesManaged.Mode 属性

If you want to pick up standard AES as security algorithm in your application, we recommend using the AesCryptoServiceProviderclass. If you want to mix the RijndaelMangedclass and AesCryptoServiceProviderclass in your application, we suggest using CBC mode instead of CFB mode in your program, since the implementation of the CBC mode in both classes is the same.

如果您想在应用程序中选择标准 AES 作为安全算法,我们建议使用 AesCryptoServiceProvider该类。如果你想在你的应用程序中混合使用RijndaelManged类和AesCryptoServiceProvider类,我们建议在你的程序中使用 CBC 模式而不是 CFB 模式,因为 CBC 模式在两个类中的实现是相同的。

回答by Jeff Moser

I think it has to do with the CipherMode.CFB. See this postdescribing AesManaged:

我认为这与 CipherMode.CFB 有关。请参阅这篇描述AesManaged 的​​帖子

AesManaged is actually just a wrapper around RinjdaelManaged with some code added to make sure that you do not setup the algorithm to operate in a non-AES compatible way. For instance, AesManaged does not allow you to change the block size. (It will also disallow the use of CFB and OFB mode because of the way that RijndaelManaged works with those modes).

AesManaged 实际上只是 RinjdaelManaged 的​​一个包装器,其中添加了一些代码以确保您不会将算法设置为以非 AES 兼容的方式运行。例如,AesManaged 不允许您更改块大小。(它也将禁止使用 CFB 和 OFB 模式,因为 RijndaelManaged 使用这些模式的方式)。

Note that if you use CipherMode.ECB or CipherMode.CBC, you'll see identical results. Any reason why you need CFB and not CBC?

请注意,如果您使用 CipherMode.ECB 或 CipherMode.CBC,您将看到相同的结果。您需要CFB而不是CBC的任何理由?

回答by SwDevMan81

Addition information from this postsays:

这篇文章的补充信息说:

Essentially, if you want to use RijndaelManaged as AES you need to make sure that:
1) The block size is set to 128 bits
2) You are not using CFB mode, or if you are the feedback size is also 128 bits
本质上,如果您想使用 RijndaelManaged 作为 AES,您需要确保:
1) 块大小设置为 128 位
2) 您没有使用 CFB 模式,或者如果您是反馈大小也是 128 位

Ok, great. I added mEncryptionType.FeedbackSize = 128; to my above example and I get an CryptographicExecption:

太好了。我添加了 mEncryptionType.FeedbackSize = 128; 到我上面的例子,我得到一个 CryptographicExecption:

System.Security.Cryptography.CryptographicException was unhandled
  Message="Bad Data.\r\n"
  Source="System.Core"
  StackTrace:
       at System.Security.Cryptography.CapiNative.SetKeyParameter(SafeCapiKeyHandle key, KeyParameter parameter, Byte[] value)
       at System.Security.Cryptography.CapiNative.SetKeyParameter(SafeCapiKeyHandle key, KeyParameter parameter, Int32 value)
       at System.Security.Cryptography.CapiSymmetricAlgorithm.SetupKey(SafeCapiKeyHandle key, Byte[] iv, CipherMode cipherMode, Int32 feedbackSize)
       at System.Security.Cryptography.CapiSymmetricAlgorithm..ctor(Int32 blockSize, Int32 feedbackSize, SafeCspHandle provider, SafeCapiKeyHandle key, Byte[] iv, CipherMode cipherMode, PaddingMode paddingMode, EncryptionMode encryptionMode)
       at System.Security.Cryptography.AesCryptoServiceProvider.CreateEncryptor(SafeCapiKeyHandle key, Byte[] iv)
       at System.Security.Cryptography.AesCryptoServiceProvider.CreateEncryptor(Byte[] key, Byte[] iv)
       at AESTest.Form1.Encrypt(Byte[] unencryptedData) in C:\Documents and Settings\nschoonmaker\My Documents\Visual Studio 2005\Projects\AESTest\AESTest\Form1.cs:line 79
       at AESTest.Form1..ctor() in C:\Documents and Settings\nschoonmaker\My Documents\Visual Studio 2005\Projects\AESTest\AESTest\Form1.cs:line 73
       at AESTest.Program.Main() in C:\Documents and Settings\nschoonmaker\My Documents\Visual Studio 2005\Projects\AESTest\AESTest\Program.cs:line 17
System.Security.Cryptography.CryptographicException was unhandled
  Message="Bad Data.\r\n"
  Source="System.Core"
  StackTrace:
       at System.Security.Cryptography.CapiNative.SetKeyParameter(SafeCapiKeyHandle key, KeyParameter parameter, Byte[] value)
       at System.Security.Cryptography.CapiNative.SetKeyParameter(SafeCapiKeyHandle key, KeyParameter parameter, Int32 value)
       at System.Security.Cryptography.CapiSymmetricAlgorithm.SetupKey(SafeCapiKeyHandle key, Byte[] iv, CipherMode cipherMode, Int32 feedbackSize)
       at System.Security.Cryptography.CapiSymmetricAlgorithm..ctor(Int32 blockSize, Int32 feedbackSize, SafeCspHandle provider, SafeCapiKeyHandle key, Byte[] iv, CipherMode cipherMode, PaddingMode paddingMode, EncryptionMode encryptionMode)
       at System.Security.Cryptography.AesCryptoServiceProvider.CreateEncryptor(SafeCapiKeyHandle key, Byte[] iv)
       at System.Security.Cryptography.AesCryptoServiceProvider.CreateEncryptor(Byte[] key, Byte[] iv)
       at AESTest.Form1.Encrypt(Byte[] unencryptedData) in C:\Documents and Settings\nschoonmaker\My Documents\Visual Studio 2005\Projects\AESTest\AESTest\Form1.cs:line 79
       at AESTest.Form1..ctor() in C:\Documents and Settings\nschoonmaker\My Documents\Visual Studio 2005\Projects\AESTest\AESTest\Form1.cs:line 73
       at AESTest.Program.Main() in C:\Documents and Settings\nschoonmaker\My Documents\Visual Studio 2005\Projects\AESTest\AESTest\Program.cs:line 17

Is there something wrong with the System.Core dll that wouldnt support this, or do I need to change something else?

System.Core dll 是否有问题不支持此功能,或者我是否需要更改其他内容?

On a side note, if I change the FeedbackSize to 8 for both, its seems to work! Even for CFB mode. So I guess my next question is, how do I get 128 to work (and hopefully this will put an end to this question)?

附带说明一下,如果我将两者的 FeedbackSize 都更改为 8,它似乎可以工作!即使对于 CFB 模式。所以我想我的下一个问题是,我如何让 128 工作(希望这会结束这个问题)?