C#AES:加密文件导致“要加密的数据长度无效。”错误

我有一个PDF文件。
当我想使用下面的代码加密它时,要加密Length of the data to encrypt is invalid. 发生了错误:

  string inputFile = @"C:\sample.pdf"; string outputFile = @"C:\sample_enc.pdf"; try { using (RijndaelManaged aes = new RijndaelManaged()) { byte[] key = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 }; byte[] iv = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 }; aes.Key = key; aes.IV = iv; aes.Mode = CipherMode.CFB; aes.Padding = PaddingMode.None; aes.KeySize = 128; aes.BlockSize = 128; using (FileStream fsCrypt = new FileStream(outputFile, FileMode.Create)) { using (ICryptoTransform encryptor = aes.CreateEncryptor(key,iv)) { using (CryptoStream cs = new CryptoStream(fsCrypt, encryptor, CryptoStreamMode.Write)) { using (FileStream fsIn = new FileStream(inputFile, FileMode.Open)) { int data; while ((data = fsIn.ReadByte()) != -1) { cs.WriteByte((byte)data); } } } } } } } catch (Exception ex) { // Length of the data to encrypt is invalid. Console.WriteLine(ex.Message); } 

使用CipherMode.CBCPaddingMode.PKCS7 ,我没有任何错误。
但是由于我的客户端,我必须使用带有无填充的 AES / CFB加密文件。

有什么想法在这里发生了什么?

块密码期望输入的长度是块大小的倍数。 使用AES,输入的长度必须是16的倍数。

您必须对明文应用某种填充,以满足此要求。 PKCS#7填充是最佳选择。

但是,第二个想法是,CFB模式将分组密码转换为流密码。 流密码不需要填充。 在这方面,.NET实现似乎已被打破。

我在这种情况下使用的(非理想的)解决方案是将明文的原始长度放入要加密的前x个字节的数据中。 然后用剩余的数据加密长度。 使用流解密时,您只需读取前x个字节,然后使用BitConverter类将它们转换回长度。 然后,它会告诉您有多少后续解密字节是您的消息的一部分。 除此之外的任何数据都可以作为填充忽略。

当您使用PaddingMode.None时,您可以自己包装ICrytoTransform并处理最终块:

 new CryptoStream(fsCrypt, new NoPaddingTransformWrapper(encryptor), CryptoStreamMode.Write) 

以下是包装类本身:

 public class NoPaddingTransformWrapper : ICryptoTransform { private ICryptoTransform m_Transform; public NoPaddingTransformWrapper(ICryptoTransform symmetricAlgoTransform) { if (symmetricAlgoTransform == null) throw new ArgumentNullException("symmetricAlgoTransform"); m_Transform = symmetricAlgoTransform; } #region simple wrap public bool CanReuseTransform { get { return m_Transform.CanReuseTransform; } } public bool CanTransformMultipleBlocks { get { return m_Transform.CanTransformMultipleBlocks; } } public int InputBlockSize { get { return m_Transform.InputBlockSize; } } public int OutputBlockSize { get { return m_Transform.OutputBlockSize; } } public int TransformBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset) { return m_Transform.TransformBlock(inputBuffer, inputOffset, inputCount, outputBuffer, outputOffset); } #endregion public byte[] TransformFinalBlock(byte[] inputBuffer, int inputOffset, int inputCount) { if (inputCount % m_Transform.InputBlockSize == 0) return m_Transform.TransformFinalBlock(inputBuffer, inputOffset, inputCount); else { byte[] lastBlocks = new byte[inputCount / m_Transform.InputBlockSize + m_Transform.InputBlockSize]; Buffer.BlockCopy(inputBuffer,inputOffset, lastBlocks, 0, inputCount); byte[] result = m_Transform.TransformFinalBlock(lastBlocks, 0, lastBlocks.Length); Debug.Assert(inputCount < result.Length); Array.Resize(ref result, inputCount); return result; } } public void Dispose() { m_Transform.Dispose(); } } 

或者你可以包装一个SymmetricAlgorithm,以便它根据填充模式在CreateEncryptor / CreateDecryptor中进行适当的包装:

 public class NoPadProblemSymmetricAlgorithm : SymmetricAlgorithm { private SymmetricAlgorithm m_Algo; public NoPadProblemSymmetricAlgorithm(SymmetricAlgorithm algo) { if (algo == null) throw new ArgumentNullException(); m_Algo = algo; } public override ICryptoTransform CreateDecryptor(byte[] rgbKey, byte[] rgbIV) { if (m_Algo.Padding == PaddingMode.None) return new NoPaddingTransformWrapper(m_Algo.CreateDecryptor(rgbKey, rgbIV)); else return m_Algo.CreateDecryptor(rgbKey, rgbIV); } public override ICryptoTransform CreateEncryptor(byte[] rgbKey, byte[] rgbIV) { if (m_Algo.Padding == PaddingMode.None) return new NoPaddingTransformWrapper(m_Algo.CreateEncryptor(rgbKey, rgbIV)); else return m_Algo.CreateEncryptor(rgbKey, rgbIV); } #region simple wrap public override void GenerateIV() { m_Algo.GenerateIV(); } public override void GenerateKey() { m_Algo.GenerateIV(); } public override int BlockSize { get { return m_Algo.BlockSize; } set { m_Algo.BlockSize = value; } } public override int FeedbackSize { get { return m_Algo.FeedbackSize; } set { m_Algo.FeedbackSize = value; } } public override byte[] IV { get { return m_Algo.IV; } set { m_Algo.IV = value; } } public override byte[] Key { get { return m_Algo.Key; } set { m_Algo.Key = value; } } public override int KeySize { get { return m_Algo.KeySize; } set { m_Algo.KeySize = value; } } public override KeySizes[] LegalBlockSizes { get { return m_Algo.LegalBlockSizes; } } public override KeySizes[] LegalKeySizes { get { return m_Algo.LegalKeySizes; } } public override CipherMode Mode { get { return m_Algo.Mode; } set { m_Algo.Mode = value; } } public override PaddingMode Padding { get { return m_Algo.Padding; } set { m_Algo.Padding = m_Algo.Padding; } } protected override void Dispose(bool disposing) { if (disposing) m_Algo.Dispose(); base.Dispose(disposing); } #endregion }