0

The code below works fine and encrypts:

hello world

to:

BgGUY2eR7GfumjbQr58tBQ==

for a run. The next time this may be different, for example:

CYIM7V/h3iXu5PYzwmQ33g==

I think this is the point of this algorithm. How do I decrypt a string that was encrypted a while back?

If I do:

static void Main(string[] args)
{
    var key = @"abcdefghijklmnopqrstuvw==";

    using (var aesAlg = Aes.Create())
    {
    aesAlg.Mode = CipherMode.CBC;
    aesAlg.Padding = PaddingMode.PKCS7;
    aesAlg.Key = Convert.FromBase64String(key);
    aesAlg.GenerateIV();

    var decryptor = aesAlg.CreateDecryptor(aesAlg.Key, aesAlg.IV);
    var helloWorld = DecryptProperty(decryptor, "CYIM7V/h3iXu5PYzwmQ33g==");
    }

}

I get:

System.Security.Cryptography.CryptographicException
  HResult=0x80131430
  Message=Padding is invalid and cannot be removed.
  Source=System.Core
  StackTrace:
   at System.Security.Cryptography.CapiSymmetricAlgorithm.DepadBlock(Byte[] block, Int32 offset, Int32 count)
   at System.Security.Cryptography.CapiSymmetricAlgorithm.TransformFinalBlock(Byte[] inputBuffer, Int32 inputOffset, Int32 inputCount)
   at System.Security.Cryptography.CryptoStream.Read(Byte[] buffer, Int32 offset, Int32 count)
   at System.IO.StreamReader.ReadBuffer()
   at System.IO.StreamReader.ReadToEnd()
   at crypt.Program.DecryptProperty(ICryptoTransform decryptor, String valueToDecrypt) 

Any ideas? How can I decrypt a string encrypted in the past using the key? Thanks!

Working code:

using System;
using System.IO;
using System.Security.Cryptography;

namespace crypt
{
    class Program
    {
        static void Main(string[] args)
        {
            var key = @"abcdefghijklmnopqrstuvw==";

            using (var aesAlg = Aes.Create())
            {
                aesAlg.Mode = CipherMode.CBC;
                aesAlg.Padding = PaddingMode.PKCS7;
                aesAlg.Key = Convert.FromBase64String(key);
                aesAlg.GenerateIV();

                var encryptor = aesAlg.CreateEncryptor(aesAlg.Key, aesAlg.IV);
                var encHelloWorld = EncryptProperty(encryptor, "hello world");

                var decryptor = aesAlg.CreateDecryptor(aesAlg.Key, aesAlg.IV);
                var helloWorld = DecryptProperty(decryptor, encHelloWorld);
            }

        }
        private static string EncryptProperty(ICryptoTransform encryptor, string valueToEncrypt)
        {
            byte[] encrypted;
            using (var msEncrypt = new MemoryStream())
            {
                using (var csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
                {
                    using (var swEncrypt = new StreamWriter(csEncrypt))
                    {
                        swEncrypt.Write(valueToEncrypt);
                    }
                    encrypted = msEncrypt.ToArray();
                }
            }
            return Convert.ToBase64String(encrypted);
        }

        private static string DecryptProperty(ICryptoTransform decryptor, string valueToDecrypt)
        {
            string decrypted;

            using (var msDecrypt = new MemoryStream(Convert.FromBase64String(valueToDecrypt)))
            {
                using (var csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read))
                {
                    using (var srDecrypt = new StreamReader(csDecrypt))
                    {
                        decrypted = srDecrypt.ReadToEnd();
                    }
                }
            }
            return decrypted;
        }
    }
}
cs0815
  • 16,751
  • 45
  • 136
  • 299
  • The answer on your last question more or less anticipates this: you cannot create new keys and IV for the Decryptor - they need to be the same as those used to encrypt. The key is usually managed by the app/OS/you the IV can be appended or prepended to the data. If the destination is not some sort of data store, find some place to store it – Ňɏssa Pøngjǣrdenlarp Jan 21 '20 at 17:07
  • Thanks, So can I obtain them and somehow hardcode them? Storage is not a problem (would go into App.config to start with). Thanks. – cs0815 Jan 21 '20 at 17:10
  • Related: https://stackoverflow.com/a/10176980/380384 – John Alexiou Jan 21 '20 at 17:15
  • Do not ever hardcode IVs or reuse them. Always create a different one and store it with the encrypted block. – Boris B. Jan 21 '20 at 17:16
  • @BorisB. thanks in our case we send the data encrypted to a 3rd party and when we get it back we decrypt it. Are you suggesting to send the IV with the encrypted data? Can I not simple store the IVs as string (?) in my app/db? Thanks. Sorry this is all new to me. – cs0815 Jan 21 '20 at 17:22
  • You can store it on your end, as long as you're able to recall it back when you need to decrypt what they send you back. – Boris B. Jan 21 '20 at 17:26
  • Thanks Boris. I read now that this bad practice and including it in the payload may not be that bad after all: https://stackoverflow.com/questions/8041451/good-aes-initialization-vector-practice – cs0815 Jan 21 '20 at 17:27
  • Sure, that's how it's typically done. It removes the need for keeping state on your end. – Boris B. Jan 21 '20 at 17:28

1 Answers1

2

You are generating a new (random) Initialization Vector on every decrypt. You need to use the same IV when decrypting that was used when encrypting.

Your sample code works because it uses the same algo state (IV included) for encrypting and decrypting in the same run, but won't work across runs because GenerateIV creates a new random buffer every time.

Typically one would write the IV before the encrypted value and then store them together.

Boris B.
  • 4,933
  • 1
  • 28
  • 59
  • Thanks. I think this code discusses it: https://stackoverflow.com/questions/8041451/good-aes-initialization-vector-practice – cs0815 Jan 21 '20 at 17:28