Reputation: 3599
Note:
The following code sample is for demonstration purposes only and implements an insecure scheme. If you are looking for a secure scheme have a look at https://stackoverflow.com/a/10177020/40347
I am using the AESCryptoServiceProvider class for testing some encryption concepts. So far in all the examples and articles out there they generate a random key to use for encryption and then immediately for decryption. Sure, it works fine because you are using the key right there, but if you encrypt, save the text and at a later time you want to decrypt it you will need the SAME key. And for that purpose also the same IV.
Now, in this code I am using the same key and IV on multiple passes, every time I run the batch that batch gives the same result (as expected). But then I close the test application and rerun the same code without change and the resulting (Base64-encoded) cypher text is different for the same input parameters, why?
I "saved" one of the B64-encoded cyphers from a previous run and fed it to the TestDecrypt method and as expected, it threw a cryptographic exception mentioning something about padding though I am sure it has to do with the fact that somehow for the same Key,IV, plain text and parameters it gives a different result on every separate run of the application.
For encrypting I have this:
public string Test(string password, Guid guid, string text)
{
const int SaltSize = 16;
string b64Cryptogram;
MD5CryptoServiceProvider md5 = new MD5CryptoServiceProvider();
Rfc2898DeriveBytes pwbytes = new Rfc2898DeriveBytes(password, SaltSize);
// Block 128-bits Key 128/192/256 bits (16/24/32 bytes)
using (AesCryptoServiceProvider aes = new AesCryptoServiceProvider())
{
aes.Padding = PaddingMode.PKCS7;
aes.Mode = CipherMode.CBC;
//aes.IV = pwbytes.GetBytes(aes.BlockSize / 8);
aes.IV = md5.ComputeHash(System.Text.Encoding.UTF8.GetBytes(password));
aes.Key = guid.ToByteArray();
ICryptoTransform encryptor = aes.CreateEncryptor(aes.Key, aes.IV);
using (MemoryStream msEncrypt = new MemoryStream())
{
using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
{
using (StreamWriter swEncrypt = new StreamWriter(csEncrypt))
{
//Write all data to the stream.
swEncrypt.Write(text);
}
b64Cryptogram = Convert.ToBase64String(msEncrypt.ToArray());
}
}
Console.WriteLine("E: {0}", b64Cryptogram);
aes.Clear();
}
return b64Cryptogram;
}
Notice I am not using the RFC2898DeriveBytes because it will randomly derive something I will no longer remember :) The idea of encrypting it is precisely that I KNOW what I used to encrypt it.
The decryption method looks like this:
public void TestDecrypt(string password, Guid guid, string ciphertextB64)
{
const int SaltSize = 16;
byte[] cipher = Convert.FromBase64String(ciphertextB64);
string plaintext;
MD5CryptoServiceProvider md5 = new MD5CryptoServiceProvider();
Rfc2898DeriveBytes pwbytes = new Rfc2898DeriveBytes(password, SaltSize);
// Block 128-bits Key 128/192/256 bits (16/24/32 bytes)
using (AesCryptoServiceProvider aes = new AesCryptoServiceProvider())
{
aes.Padding = PaddingMode.PKCS7;
aes.Mode = CipherMode.CBC;
//aes.IV = pwbytes.GetBytes(aes.BlockSize / 8);
aes.IV = md5.ComputeHash(System.Text.Encoding.UTF8.GetBytes(password));
aes.Key = guid.ToByteArray();
ICryptoTransform decryptor = aes.CreateDecryptor(aes.Key, aes.IV);
using (MemoryStream msEncrypt = new MemoryStream(cipher))
{
using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, decryptor, CryptoStreamMode.Read))
{
using (StreamReader swEncrypt = new StreamReader(csEncrypt))
{
plaintext = swEncrypt.ReadToEnd();
}
}
}
Console.WriteLine("D: {0}", plaintext);
aes.Clear();
}
}
Now, just put that in a console application and run it. Then exit and run it again and you will see that for the same Mode, Padding, IV, Key and plain text data, the output cipher will not be the same on every application run. They will be the same provided you run the method repeatedly in the same run of the application.
In case it was not obvious, here is the console code I used to test:
Guid guid = Guid.NewGuid();
string plain = "Text to be encrypted 123458970";
string password = "This is a test of the emergency broadcast system";
TestDecrypt(password, guid, Test(password, guid, plain));
TestDecrypt(password, guid, Test(password, guid, plain));
Test(password, guid, plain);
Test(password, guid, plain);
Test(plain, guid, password);
TestDecrypt(password, guid, "W4Oi0DrKnRpxFwtE0xVbYJwWgcA05/Alk6LrJ5XIPl8=");
}
Upvotes: 0
Views: 1340
Reputation: 759
The solution here is to pull in from a stored or constant Guid
. Calling
Guid.NewGuid();
will return a different result every time. From the docs:
This is a convenient static method that you can call to get a new Guid. The method wraps a call to the Windows CoCreateGuid function. The returned Guid is guaranteed to not equal Guid.Empty.
Alternatively when testing you can use Guid.Empty which will return all zeroes.
Or, you can store it as such using its string constructor overload:
var guid = new Guid("0f8fad5b-d9cb-469f-a165-70867728950e");
Upvotes: 2