2015-09-06 165 views
2

考虑以下测试:解密AES密文

[Test] 
    public void TestAes256EcbPkcs7Stream() 
    { 
     // 504 bytes of plain text 
     const string inputString = new string('z', 504); 
     var inputBytes = Encoding.UTF8.GetBytes(inputString); 
     byte[] key = { 
      0, 0, 0, 0, 0, 0, 0, 0, 
      1, 0, 0, 0, 0, 0, 0, 0, 
      2, 0, 0, 0, 0, 0, 0, 0, 
      3, 0, 0, 0, 0, 0, 0, 0 
     }; 
     var rij = new RijndaelManaged 
     { 
      BlockSize = 256, // 256 bits == 32 bytes 
      Key = key, 
      IV = key, // just for test 
      Mode = CipherMode.ECB, 
      Padding = PaddingMode.PKCS7 
     }; 
     var enc = rij.CreateEncryptor(); 
     var encBytes = enc.TransformFinalBlock(inputBytes, 0, inputBytes.Length); 
     Assert.AreEqual(512, encBytes.Length); 
     var dec = rij.CreateDecryptor(); 
     byte[] decBytes = new byte[inputBytes.Length]; 
     int decPos = 0; 
     using (var cipherMs = new MemoryStream(encBytes)) 
     { 
      var buf = new byte[32]; 
      // process all blocks except the last one 
      while (cipherMs.Read(buf, 0, buf.Length)==buf.Length && 
       cipherMs.Length!=cipherMs.Position) 
      { 
       for (int w = 0; w!=buf.Length;) 
       { 
        w += dec.TransformBlock(buf, 0, buf.Length, decBytes, decPos); 
        decPos += w; 
       } 
      } 
      // ensure that we read all blocks 
      Assert.IsTrue(cipherMs.Length==cipherMs.Position); 
      // process the last block 
      var tailBytes = dec.TransformFinalBlock(buf, 0, buf.Length); 
      // here decPos==480, that means 480 bytes were written to decBytes 
      // and 504-480 = 24 bytes come from TransformFinalBlock 
      Assert.AreEqual(24, tailBytes.Length); // <- fail, because the actual length is 56 
      Buffer.BlockCopy(tailBytes, 0, decBytes, decPos, tailBytes.Length); 
     } 
     Assert.AreEqual(inputBytes, decBytes); 
    } 

出于某种原因,我得到了56个字节的最后一个模块,而不是24个字节。 我想,TransformBlock/TransformFinalBlock应以其他方式使用,但不幸的是,MSDN文档没有解释太多这些方法。 有什么想法?

+0

@ArtjomB。纯文本大小为504字节,解码480字节,剩余504-480 = 24字节。这些24个字节应该由TransformFinalBlock返回。另一方面,56字节不适合32字节的密码块。 – nitrocaster

+0

有趣。请记住,使用PaddingMode.PKCS7,您的504字节起始大小不再适用。为了加密明文,密码将其填充到512字节。对于32字节块大小或更常用的16字节块大小,情况将会如此。我仍然在考虑TransformFinalBlock的行为。 – WDS

+0

因此,当while循环读入密文时,缓冲区获得最后32个字节(当我们考虑填充时,为480 - 512),但while条件失败,因此decPos不会在那里增加。我仍然无法得到TransformFinalBlock如何产生56个字节。我本来预计tailBytes在这一点上是32,然后你会去掉填充。通常所有这些对程序员都是不可见的,因为你只是使用CryptoStream,但这是一个有趣的难题。 – WDS

回答

0

好,事情是这样的:当 被TransformBlock称为第一次,它复制输入缓冲器所谓depad缓冲的最后一块,然后转换并写入剩余块到输出缓冲区。在下一次调用期间,它将数据从depad缓冲区转换为数据,将其写入输出缓冲区,再次将输入缓冲区的最后一个块复制到depad缓冲区,并将转换后的剩余块写入输出,就像第一次一样。

TL; DR:TransformBlock缓存输入数据的最后一个块,所以当TransformFinalBlock被调用时,它可以抓住的最后一个块,并删除填充。如果没有缓存,最后的块可能由TransformBlock处理。在这种情况下,填充不会被删除。