Вопрос

I have a PDF File.
When I want to encrypt it using codes below the Length of the data to encrypt is invalid. error occurred:

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


With CipherMode.CBC and PaddingMode.PKCS7, I don't have any errors.
But because of my client, I have to encrypt the file using AES/CFB with No Padding.

Any ideas what's happening here?

Это было полезно?

Решение

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.

Другие советы

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

}

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.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top