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

34
我运行的示例代码如下,与模式、填充、块大小和密钥大小相同。我使用相同的初始化向量、密钥和数据。
使用`RijndaelManaged`生成的加密结果为: 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
使用`AesCryptoServiceProvider`生成的加密结果为: 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
以下是产生这些结果的代码。
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();
      //设置加密模式为CFB
      mEncryptionType.Mode = CipherMode.CFB;
      //设置填充方式为PKCS7
      mEncryptionType.Padding = PaddingMode.PKCS7;
      //设置加密块大小为128位
      mEncryptionType.BlockSize = 128;
      //设置密钥大小为256位
      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) { // 创建内存流以存储结果 MemoryStream mem_stream = new MemoryStream(); // 创建加密流进行转换 CryptoStream crypto_stream = new CryptoStream(mem_stream, cryptoTransform, CryptoStreamMode.Write); // 写入数据并进行转换 crypto_stream.Write(dataToTransform, 0, dataToTransform.Length); // 刷新缓冲区并写入最终块 crypto_stream.FlushFinalBlock(); // 将转换后的内存流转换回字节数组 result = mem_stream.ToArray(); // 关闭流 mem_stream.Close(); crypto_stream.Close(); } return result; } }
说明:该代码段展示了一个使用 AES 算法进行数据加密的方法。在此之前,先通过设置加密模式、填充模式、块大小和密钥大小等参数来配置对称加密算法。然后,使用给定的私钥和初始化向量创建加密器,并使用 CryptoStream 进行数据转换。在写入待加密数据并刷新缓冲区之后,将加密后的数据存储在内存流中,并转换为字节数组返回。

我猜想我是否漏掉了什么。

更新:结果发现,如果您尝试将CipherMode设置为CFB,则AesManaged会抛出CryptographicException(“指定的密码模式对于此算法无效”)。 我认为AesCryptoServiceProvider也应该这样做,但它没有。看起来很奇怪,FIPS认证类允许无效的密码模式。


1
这个问题是从以下讨论中产生的:https://dev59.com/1nNA5IYBdhLWcg3wfd8m - Jeff Moser
3个回答

47

来自Microsoft的回应:

RijndaelManaged类和AesCryptoServiceProvider类是两种不同的实现。 RijndaelManaged类是在.net框架中Rijndael算法的一种实现,未经过美国国家标准与技术研究院(NIST)加密模块验证计划(CMVP)的认证。

然而, AesCryptoServiceProvider类调用了Windows加密API,使用RSAENH.DLL,并且已通过NIST在CMVP中进行了验证。虽然Rijndael算法是NIST竞赛中选定作为AES算法的胜者,但Rijndael和官方AES之间仍存在一些差异。 因此,RijndaelManaged类和AesCryptoServiceProvider类在实现上存在细微的差别。

此外,RijndaelManaged类无法提供与AES等效的实现。另一个在.net框架中实现的类是AesManaged类。此类只需使用固定的块大小和迭代次数包装RijndaelManaged类即可达到AES标准。然而,它不支持反馈大小,特别是当模式设置为CFB或OFB时,将抛出CryptographicException异常。

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

AesManaged类AesManaged.Mode属性

如果您想在应用程序中选择标准AES作为安全算法,我们建议使用AesCryptoServiceProvider类。如果您想在应用程序中混合使用RijndaelManged类和AesCryptoServiceProvider类,我们建议使用CBC。

在你的程序中,应该使用CBC模式而不是CFB模式,因为两个类中的CBC模式实现是相同的。


7
我认为这与CipherMode.CFB有关。请参阅此帖子,其中描述了AesManaged
AesManaged实际上只是RinjdaelManaged的包装器,并添加了一些代码以确保您不会以非AES兼容的方式设置算法。例如,AesManaged不允许您更改块大小。(它还禁止使用CFB和OFB模式,因为RijndaelManaged与这些模式的工作方式不同)。
请注意,如果您使用CipherMode.ECB或CipherMode.CBC,则会看到相同的结果。是否有任何原因需要CFB而不是CBC?

是的,我们购买的用于与软件接口的VHDL算法仅支持CFB模式。 - SwDevMan81
我假设AesCryptoServiceProvider是与您的客户端一起使用的那个? - Jeff Moser
是的,根据先前的讨论,客户需要的是符合 FIPS 标准的版本。 - SwDevMan81

2

来自这篇文章的补充信息如下:

基本上,如果您想将RijndaelManaged用作AES,则需要确保:
1)块大小设置为128位
2)您没有使用CFB模式,或者如果您正在使用,则反馈大小也为128位。

好的。我在上面的示例中添加了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.Core dll有问题吗?还是我需要改变其他什么东西?

顺便说一句,如果我将FeedbackSize都改为8,则似乎可以工作!即使是CFB模式。所以,我的下一个问题是,如何让128起作用(希望这会结束这个问题)?


希望我能够,但是 VHDL 算法似乎只支持 128 的 FeedbackSize :( - SwDevMan81
也许 FeedbackSize 是以字节而非位表示的。8*16 = 128。 - Cheeso
文档说它是以位为单位的,但是嘿,也许是错的 ;) - SwDevMan81
这仍然是位(bit)。FeedbackSize 是内部反馈寄存器每次与输入进行异或操作时移位的位数。 - Jeff Moser

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接