1

I have been doing some research into using AESCryptoServiceProvider in C#. So far, I have an implementation which seems to be working. However, my understanding of initialization vectors is that it should protect my cipher text from being the same when the payload and key are the same, but it appears I am doing something wrong. Changing my initialization vector doesn't seem to affect the results of these functions at all.

Here are my functions:

public string EncryptString(string toEncrypt, byte[] encryptionKey, byte[] iv)
{
    var toEncryptBytes = Encoding.Default.GetBytes(toEncrypt);
    using (var aes = new AesCryptoServiceProvider())
    {
        aes.Key = encryptionKey;
        aes.Mode = CipherMode.CBC;
        aes.Padding = PaddingMode.PKCS7;
        //aes.GenerateIV();
        aes.IV = iv;

        using (var encryptor = aes.CreateEncryptor(encryptionKey, aes.IV))
        using (var ms = new MemoryStream())
        {
            using (var cs = new CryptoStream(ms, encryptor, CryptoStreamMode.Write))
            using (var bWriter = new BinaryWriter(cs))
            {
                bWriter.Write(aes.IV, 0, aes.IV.Length);
                bWriter.Write(toEncryptBytes, 0, toEncryptBytes.Length);
                cs.FlushFinalBlock();
            }
            return Convert.ToBase64String(ms.ToArray());
        }
    }
}

public string DecryptString(string toDecrypt, byte[] encryptionKey, byte[] iv)
{
    using (var aes = new AesCryptoServiceProvider())
    {
        aes.Key = encryptionKey;
        aes.Mode = CipherMode.CBC;
        aes.Padding = PaddingMode.PKCS7;
        var toDecryptBytes = Convert.FromBase64String(toDecrypt);

        Array.Copy(toDecryptBytes, 0, iv, 0, iv.Length);
        aes.IV = iv;

        using (var ms = new MemoryStream())
        {
             using (var cs = new CryptoStream(ms, aes.CreateDecryptor(aes.Key, iv), CryptoStreamMode.Write))
             using (var binWriter = new BinaryWriter(cs))
             {
                 binWriter.Write(toDecryptBytes, iv.Length, toDecryptBytes.Length - iv.Length);
             }
             return Encoding.Default.GetString(ms.ToArray());
        }
    }
}

New version of my encrypt function I have now following the advice from @owlstead, changing back CBC mode. This does appear to be working correctly now using CBC.

public string EncryptString(string toEncrypt, byte[] encryptionKey)
{
    var toEncryptBytes = Encoding.Default.GetBytes(toEncrypt);
    using (var aes = new AesCryptoServiceProvider())
    {
        aes.Key = encryptionKey;
        aes.Mode = CipherMode.CBC;
        aes.Padding = PaddingMode.PKCS7;
        aes.GenerateIV();

        using (var encryptor = aes.CreateEncryptor(encryptionKey, aes.IV))
        using (var ms = new MemoryStream())
        {
             ms.Write(aes.IV, 0, aes.IV.Length);
             using (var cs = new CryptoStream(ms, encryptor, CryptoStreamMode.Write))
             using (var bWriter = new BinaryWriter(cs))
             {
                 bWriter.Write(toEncryptBytes, 0, toEncryptBytes.Length);
                 cs.FlushFinalBlock();
             }
             return Convert.ToBase64String(ms.ToArray());
        }
    }
}
Adam Shaver
  • 133
  • 1
  • 6
  • "doesn't seem to affect the results of these functions at all." Do you mean that same inputs give the same outputs? – TomF Aug 22 '13 at 19:36
  • Different IV inputs give the same outputs. For example, I encrypt some data using a key of "AAABBBCCCDDDTOIGJAEOITGJIOEAJTGA" and an IV of "AAABBBCCCDDDTOI5". This gives me back "WIZzTtGBynNs/lN7Yqs4Gb8hDHInW4fgpCTOWUifIwc=" as my cipher text. I passed this to my decrypt method with the same key but a different IV and got the original text back. Another point worth mentioning, is that changing the IV in my call to the EncryptString function will generate the same ciphertext. – Adam Shaver Aug 22 '13 at 19:41
  • I commented this before but it was not posted. First of all your choice of IV is unnecessarily complicated, u can just set the first byte of an array of zeros. Also if you are absolutely sure that the AES module works that you are probably using it wrong, maybe your IV is always zeros, or something, debug. – TomF Aug 22 '13 at 19:53
  • Wouldn't only randomly setting the first byte of my IV byte array, and thus leaving the rest as zeros, reduce the number of possible outputs from the algorithm using the same key and same plaintext to 255? – Adam Shaver Aug 22 '13 at 20:39
  • No, just use a different IV, every time, it doesn't matter what the IV will be, you are right that changing just the first byte can cause same values, but i'm saying that for debug or for a starting IV, you don't have to really go so crazy. Example, just increase the bytes, all 16 of them one at a time, and put the length of the clear data on the first 2 bytes or something. – TomF Aug 22 '13 at 20:40
  • I have changed my cipher mode of operation to use Cipher Feedback (CipherMode.CFB) and now is behaving as expected. I also realized that my IV input to the DecryptString method was superfluous since the decrypt function is setting the IV itself from the toDecrypt field. – Adam Shaver Aug 22 '13 at 20:57
  • @AdamShaver I'm glad that you found a solution! But please take a look at my answer; it may be of importance with the other mode as well, and in general there was little wrong with using CBC. I would urge you also to try and add an authentication tag if you want to use the encrypted string in any communication protocol. – Maarten Bodewes Aug 23 '13 at 10:07

2 Answers2

2

You are putting the CryptoStream and the BinaryWriter in the wrong order for at least the encrypt function. You are currently encrypting your IV, while the IV should be put in front of the ciphertext, in plain.

The IV is XORed with the plain text before encryption. Now, this means that you zero out the plain text: you are encrypting a zero valued block.

Maarten Bodewes
  • 90,524
  • 13
  • 150
  • 263
0

CipherMode.ECB mode seems not picking up the IV argument(which means no matter what IV you passed in, the result will be the same),this is possible because ECB doesn't need a concept as Initial Vector,for there's no subsequent vectors either. If you want to mask your ECB block, XOR your block mannually before encryption is OK. While you changed to CipherMode.CBC, the IV does matters,for subsequent blocks will be XORed with previous encrypted block ,as forms a chain.

Xiaoyuvax
  • 71
  • 6