2

While playing with some AES C# wrappers found on SO and msdn (most notable here and here and here) I wrote the following code and I made the mistake of writing the IV to the CryptoStream.

What I noticed is that the output byte array contains the same values when the IV is written to the CryptoStream. If I comment out the line cryptoStream.Write(aes.IV, 0, aes.IV.Length);, it's fine, the output will be different.

My question is why in this case the output is the same? I realize that writing the IV to the CryptoStream is not what I am supposed to do but I find it odd especially given that the IV is different every time the function executes.

TestEncryption.cs:

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;

namespace Test
{
  public class TestEncryption
  {

    public static readonly int KeyBitSize = 256;

    private static readonly int BlockBitSize = 128;
    public static readonly byte[] _salt = new byte[] { 23, 13, 23, 213, 15, 193, 134, 147, 223, 151 };
    const int Iterations = 10000;


    public static string Encrypt(byte[] inputBytes, string password)
    {

      using (var aes = new AesManaged

      {
        KeySize = KeyBitSize,
        BlockSize = BlockBitSize,
        Mode = CipherMode.CBC,
        Padding = PaddingMode.PKCS7,
      })
      {
        var cryptKey = CreateKey(password);
        aes.GenerateIV();
        Console.WriteLine("IV={0}", string.Join(", ", aes.IV.Select(b => b.ToString())));
        using (var encrypter = aes.CreateEncryptor(cryptKey, aes.IV))
        using (var output = new MemoryStream())
        {
          using (var cryptoStream = new CryptoStream(output, encrypter, CryptoStreamMode.Write))
          {
            cryptoStream.Write(aes.IV, 0, aes.IV.Length);
            cryptoStream.Write(inputBytes, 0, inputBytes.Length);
            cryptoStream.FlushFinalBlock();
          }
          Console.WriteLine("Output={0}", string.Join(", ", output.ToArray().Select(b => b.ToString())));
          return Convert.ToBase64String(output.ToArray());
        }

      }
    }
    public static string Encrypt(string input, string password)
    {
      return Encrypt(Encoding.UTF8.GetBytes(input), password);
    }


    public static byte[] CreateKey(string password)
    {

      using (var rfc2898DeriveBytes = new Rfc2898DeriveBytes(password, _salt, Iterations))
        return rfc2898DeriveBytes.GetBytes(32);
    }
  }
}

Program.cs

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;

namespace Test
{
  class Program
  {
    static void Main(string[] args)
    { 
      //Test1();

      Test2();
      Console.ReadLine();
    }

    private static void Test2()
    {

      string text = "some longer text";
      string pwd = "test2";

      String encrypt1 = TestEncryption.Encrypt(text, pwd);
      String encrypt2 = TestEncryption.Encrypt(text, pwd);

      Console.WriteLine(encrypt1 == encrypt2);
    }
 }

}

Community
  • 1
  • 1
boggy
  • 3,674
  • 3
  • 33
  • 56
  • As [Wikipedia](https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#CBC) says, in CBC cipher mode first block of data get XORed with IV. And `IV xor IV = 0`. – user4003407 Nov 17 '16 at 02:03
  • The IV does not need to be secret and a usual procedure is to prefix the encrypted data with the IV so it will be available for decryption. Additionally the IV should be a random byte array, generally obtained form a cryptographic pseudo random number generator (CPRNG) – zaph Nov 17 '16 at 02:32

0 Answers0