C# AES: Encrypt a file causes “Length of the data to encrypt is invalid.” error

筅森魡賤 提交于 2019-11-28 11:47:40

A block cipher expects input with a length that is a multiple of the block size. With AES, the length of the input must be a multiple of 16.

You must apply some sort of padding to the plaintext so that this requirement is satisfied. PKCS#7 padding is the best choice.

However, on second thought, CFB mode turns a block cipher into a stream cipher. Stream ciphers don't need padding. The .NET implementation seems to be broken in this regard.

A (not ideal) solution I have used in this situation is to place the raw length of the plaintext into the first x bytes of data to be encrypted. The length then is encrypted with the rest of the data. When decrypting using the stream you simply read the first x bytes and convert them using the BitConverter class back to the length. This then tells you how many subsequent decrypted bytes are part of your message. Any data beyond that can be ignored as padding.

When you use PaddingMode.None you can wrap the ICrytoTransform and handle final block yourself:

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

The following is a wrapper class itself:

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();
    }
}

OR you can wrap a SymmetricAlgorithm so that it will do appropriate wrapping in CreateEncryptor/CreateDecryptor depending on padding mode:

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

}
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!