Шифрование и дешифрование строки в C#: простой и безопасный способ
Правильное шифрование строк с помощью AES в C#
Привет! Твой XOR-метод действительно небезопасен — любой может расшифровать данные, зная только сам алгоритм. Для защиты конфиденциальных данных (например, паролей) правильнее использовать AES (Advanced Encryption Standard) с длиной ключа 256 бит. Но запомни главное правило: пароли нельзя хранить в обратимом виде — используй хеширование (например, bcrypt) для аутентификации. Если же тебе действительно нужно обратимое шифрование (например, для хранения токенов), вот безопасный пример.
Полный код шифрования/дешифрования строки через AES-256
using System;
using System.IO;
using System.Security.Cryptography;
using System.Text;
public static class StringCipher
{
// Размеры ключа и соли (в байтах)
private const int KeySize = 256; // бит
private const int SaltSize = 16; // 128 бит
private const int IvSize = 16; // 128 бит для AES
/// <summary>
/// Шифрует строку с использованием AES-256 и случайного IV.
/// </summary>
/// <param name="plainText">Исходный текст</param>
/// <param name="passPhrase">Парольная фраза (ключ)</param>
/// <returns>Base64-строка: соль + IV + зашифрованные данные</returns>
public static string Encrypt(string plainText, string passPhrase)
{
// 1. Генерируем случайную соль и IV
byte[] salt = new byte[SaltSize];
byte[] iv = new byte[IvSize];
using (var rng = RandomNumberGenerator.Create())
{
rng.GetBytes(salt);
rng.GetBytes(iv);
}
// 2. Создаём ключ из парольной фразы с помощью PBKDF2
byte[] key;
using (var deriveBytes = new Rfc2898DeriveBytes(passPhrase, salt, 100000, HashAlgorithmName.SHA256))
{
key = deriveBytes.GetBytes(KeySize / 8);
}
// 3. Шифруем
byte[] encrypted;
using (var aes = Aes.Create())
{
aes.KeySize = KeySize;
aes.Key = key;
aes.IV = iv;
aes.Mode = CipherMode.CBC;
aes.Padding = PaddingMode.PKCS7;
using (var ms = new MemoryStream())
using (var cs = new CryptoStream(ms, aes.CreateEncryptor(), CryptoStreamMode.Write))
{
byte[] plainBytes = Encoding.UTF8.GetBytes(plainText);
cs.Write(plainBytes, 0, plainBytes.Length);
cs.FlushFinalBlock();
encrypted = ms.ToArray();
}
}
// 4. Объединяем: соль + IV + шифротекст
byte[] result = new byte[salt.Length + iv.Length + encrypted.Length];
Buffer.BlockCopy(salt, 0, result, 0, salt.Length);
Buffer.BlockCopy(iv, 0, result, salt.Length, iv.Length);
Buffer.BlockCopy(encrypted, 0, result, salt.Length + iv.Length, encrypted.Length);
return Convert.ToBase64String(result);
}
/// <summary>
/// Дешифрует строку, зашифрованную методом Encrypt.
/// </summary>
/// <param name="cipherText">Base64-строка: соль + IV + зашифрованные данные</param>
/// <param name="passPhrase">Парольная фраза (та же, что при шифровании)</param>
/// <returns>Исходный текст</returns>
public static string Decrypt(string cipherText, string passPhrase)
{
// 1. Извлекаем соль, IV и шифротекст из Base64
byte[] fullCipher = Convert.FromBase64String(cipherText);
byte[] salt = new byte[SaltSize];
byte[] iv = new byte[IvSize];
byte[] encrypted = new byte[fullCipher.Length - salt.Length - iv.Length];
Buffer.BlockCopy(fullCipher, 0, salt, 0, salt.Length);
Buffer.BlockCopy(fullCipher, salt.Length, iv, 0, iv.Length);
Buffer.BlockCopy(fullCipher, salt.Length + iv.Length, encrypted, 0, encrypted.Length);
// 2. Восстанавливаем ключ (те же параметры PBKDF2)
byte[] key;
using (var deriveBytes = new Rfc2898DeriveBytes(passPhrase, salt, 100000, HashAlgorithmName.SHA256))
{
key = deriveBytes.GetBytes(KeySize / 8);
}
// 3. Дешифруем
string plainText;
using (var aes = Aes.Create())
{
aes.KeySize = KeySize;
aes.Key = key;
aes.IV = iv;
aes.Mode = CipherMode.CBC;
aes.Padding = PaddingMode.PKCS7;
using (var ms = new MemoryStream(encrypted))
using (var cs = new CryptoStream(ms, aes.CreateDecryptor(), CryptoStreamMode.Read))
using (var sr = new StreamReader(cs, Encoding.UTF8))
{
plainText = sr.ReadToEnd();
}
}
return plainText;
}
}
Как это работает?
- Соль и IV — генерируются случайно для каждого вызова. Это гарантирует, что одинаковый текст будет давать разные шифротексты.
- PBKDF2 — функция растяжения ключа. Парольная фраза превращается в криптостойкий ключ. 100 000 итераций замедляют атаки перебором.
- Режим CBC — стандартный режим блочного шифрования, безопасный при правильном использовании IV.
- Результат — соль и IV хранятся вместе с шифротекстом, поэтому для дешифрования нужен только пароль.
Пример использования
string original = "Мой секретный пароль!";
string password = "SuperSecretKey123";
string encrypted = StringCipher.Encrypt(original, password);
Console.WriteLine($"Зашифровано: {encrypted}");
string decrypted = StringCipher.Decrypt(encrypted, password);
Console.WriteLine($"Расшифровано: {decrypted}");
Важно: Храни парольную фразу в безопасном месте (например, в конфигурации приложения с ограниченным доступом). Для хранения паролей пользователей используй хеширование с солью (например, BCrypt.Net), а не шифрование.
Альтернативный подход: использование Data Protection API (DPAPI) для Mono и .NET
Если ты работаешь в среде Windows или Mono (Linux) и тебе не нужно передавать зашифрованные данные между разными машинами, можно использовать Data Protection API (DPAPI). Он автоматически управляет ключами, используя учётные данные текущего пользователя или машины. Это проще в реализации, но привязано к конкретной среде.
using System.Security.Cryptography;
using System.Text;
public static class DpapiCipher
{
public static string Encrypt(string plainText)
{
byte[] plainBytes = Encoding.UTF8.GetBytes(plainText);
byte[] encrypted = ProtectedData.Protect(plainBytes, null, DataProtectionScope.CurrentUser);
return Convert.ToBase64String(encrypted);
}
public static string Decrypt(string cipherText)
{
byte[] encrypted = Convert.FromBase64String(cipherText);
byte[] plainBytes = ProtectedData.Unprotect(encrypted, null, DataProtectionScope.CurrentUser);
return Encoding.UTF8.GetString(plainBytes);
}
}
Плюсы и минусы
- Плюсы: Не нужно управлять ключами, солью и IV — всё делает ОС. Код очень короткий.
- Минусы: Данные нельзя расшифровать на другой машине или под другим пользователем. Работает только на Windows (встроенная поддержка) и Mono (через криптографи