Примечание:
Следующий пример кода предназначен только для демонстрационных целей и реализует небезопасную схему. Если вы ищете безопасную схему, посмотрите https://stackoverflow.com/a/10177020/40347.
Я использую класс AESCryptoServiceProvider для тестирования некоторых концепций шифрования. До сих пор во всех примерах и статьях они генерировали случайный ключ для шифрования, а затем сразу для расшифровки. Конечно, это работает нормально, потому что вы используете ключ прямо здесь, но если вы зашифруете, сохраните текст, а позже вы захотите его расшифровать, вам понадобится ТОТ ЖЕ ключ. А для этого и тот же IV.
Теперь в этом коде я использую один и тот же ключ и IV на нескольких проходах, каждый раз, когда я запускаю пакет, этот пакет дает один и тот же результат (как и ожидалось). Но затем я закрываю тестовое приложение и повторно запускаю тот же код без изменений, и результирующий (в кодировке Base64) зашифрованный текст отличается для тех же входных параметров, почему?
Я «сохранил» один из шифров с кодировкой B64 из предыдущего запуска и передал его методу TestDecrypt, и, как и ожидалось, он выдал криптографическое исключение, упоминающее что-то о дополнении, хотя я уверен, что это связано с тем фактом, что каким-то образом для один и тот же ключ, IV, обычный текст и параметры дают разные результаты при каждом отдельном запуске приложения.
Для шифрования у меня есть это:
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;
}
Обратите внимание, что я не использую RFC2898DeriveBytes, потому что он случайным образом выведет что-то, что я больше не вспомню :) Идея его шифрования заключается именно в том, что я ЗНАЮ, что я использовал для его шифрования.
Метод расшифровки выглядит так:
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();
}
}
Теперь просто поместите это в консольное приложение и запустите. Затем выйдите и запустите его снова, и вы увидите, что для одного и того же режима, заполнения, IV, ключа и данных в виде обычного текста выходной шифр не будет одинаковым при каждом запуске приложения. Они будут одинаковыми, если вы повторно запускаете метод в одном и том же запуске приложения.
Если это не очевидно, вот код консоли, который я использовал для тестирования:
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=");
}