Симметричное шифрование основной строки .Net

Мне нужно хранить конфиденциальные строки в БД, поэтому я решил зашифровать их в БД и расшифровать на уровне приложения. И заметил, что не так просто найти готовый пример. Пожалуйста, проверьте мой код ниже. Он основан на этого Дамьенбода тему, но я включил IV в саму строку и также хочу знать, готова ли она к производству. И несколько несвязанный вопрос: возможно, мне следует использовать старое решение Rijndael, например это?

public class SymmetricEncryptDecrypt
{
    private const int ivBytes = 128;

    public (string Key, string IVBase64) InitSymmetricEncryptionKeyIV()
    {
        var key = GetEncodedRandomString(32); // 256
        using (Aes cipher = CreateCipher(key))
        {
            cipher.GenerateIV();
            var IVBase64 = Convert.ToBase64String(cipher.IV);
            return (key, IVBase64);
        }
    }

    private byte[] GetNewIv()
    {
        using (Aes cipher = CreateCipher(GetEncodedRandomString(32)))
        {
            cipher.GenerateIV();
            return cipher.IV;
        }
    }

    /// <summary>
    /// Encrypt using AES
    /// </summary>
    /// <param name="text">any text</param>
    /// <param name="IV">Base64 IV string/param>
    /// <param name="key">Base64 key</param>
    /// <returns>Returns an encrypted string</returns>
    public string Encrypt(string text, string key)
    {
        var iv = this.GetNewIv();

        using (Aes cipher = CreateCipher(key))
        {
            cipher.IV = iv;

            ICryptoTransform cryptTransform = cipher.CreateEncryptor();
            byte[] plaintext = Encoding.UTF8.GetBytes(text);
            byte[] cipherText = cryptTransform.TransformFinalBlock(plaintext, 0, plaintext.Length);

            return Convert.ToBase64String(iv.Concat(cipherText).ToArray());
        }
    }

    /// <summary>
    /// Decrypt using AES
    /// </summary>
    /// <param name="text">Base64 string for an AES encryption</param>
    /// <param name="key">Base64 key</param>
    /// <returns>Returns a string</returns>
    public string Decrypt(string encryptedText, string key)
    {
        var cipherTextBytesWithSaltAndIv = Convert.FromBase64String(encryptedText);

        var ivStringBytes = cipherTextBytesWithSaltAndIv.Take(ivBytes / 8).ToArray();
        // Get the actual cipher text bytes by removing the first 64 bytes from the cipherText string.
        var cipherTextBytes = cipherTextBytesWithSaltAndIv.Skip(ivBytes / 8).Take(cipherTextBytesWithSaltAndIv.Length - (ivBytes / 8)).ToArray();

        using (Aes cipher = CreateCipher(key))
        {
            cipher.IV = ivStringBytes;

            ICryptoTransform cryptTransform = cipher.CreateDecryptor();
            byte[] plainBytes = cryptTransform.TransformFinalBlock(cipherTextBytes, 0, cipherTextBytes.Length);

            return Encoding.UTF8.GetString(plainBytes);
        }
    }

    private string GetEncodedRandomString(int length)
    {
        var base64 = Convert.ToBase64String(GenerateRandomBytes(length));
        return base64;
    }

    /// <summary>
    /// Create an AES Cipher using a base64 key
    /// </summary>
    /// <param name="key"></param>
    /// <returns>AES</returns>
    private Aes CreateCipher(string keyBase64)
    {
        // Default values: Keysize 256, Padding PKC27
        Aes cipher = Aes.Create();
        cipher.Mode = CipherMode.CBC; // Ensure the integrity of the ciphertext if using CBC
        cipher.Padding = PaddingMode.ISO10126;
        cipher.Key = Convert.FromBase64String(keyBase64);
        return cipher;
    }

    private byte[] GenerateRandomBytes(int length)
    {
        var byteArray = new byte[length];
        RandomNumberGenerator.Fill(byteArray);
        return byteArray;
    }
}

1 ответ
1

Мне этот код кажется сложным. Не потому, что я не знаю, как использовать шифровальщик AES, но он содержит много избыточности. От настройки CipherMode.CBC который по умолчанию GetNewIv() который по умолчанию является случайным.

Много ли кода нацелено на улучшение безопасности? Если это так, у вас есть ключ для хранения взлома в string. Я могу легко получить ключ из памяти приложения, даже если он в настоящее время не используется, потому что string неизменяем и может храниться в памяти в течение неопределенного периода времени. Хранение ключа в byte[] массив позволяет очистить массив в любой момент.

Если безопасности на стороне БД достаточно, вы можете упростить код следующими двумя способами.

Я использую стримы, потому что это мне удобнее. В качестве бонуса код можно легко перенести для использования в потоках.

private static string Encrypt(string text, byte[] key)
{
    using AesManaged aes = new AesManaged() { Key = key, Padding = PaddingMode.ISO10126 };
    using MemoryStream ms = new MemoryStream();
    ms.Write(aes.IV);
    using (CryptoStream cs = new CryptoStream(ms, aes.CreateEncryptor(), CryptoStreamMode.Write, true))
    {
        cs.Write(Encoding.UTF8.GetBytes(text));
    }
    return Convert.ToBase64String(ms.ToArray());
}

private static string Decrypt(string base64, byte[] key)
{
    using MemoryStream ms = new MemoryStream(Convert.FromBase64String(base64));
    byte[] iv = new byte[16];
    ms.Read(iv);
    using AesManaged aes = new AesManaged() { Key = key, IV = iv, Padding = PaddingMode.ISO10126 };
    using CryptoStream cs = new CryptoStream(ms, aes.CreateDecryptor(), CryptoStreamMode.Read, true);
    using MemoryStream output = new MemoryStream();
    cs.CopyTo(output);
    return Encoding.UTF8.GetString(output.ToArray());
}

Демо

string text = "Hello world";
byte[] key = Enumerable.Range(0, 32).Select(x => (byte)x).ToArray(); // just for example :)
string base64 = Encrypt(text, key);
Console.WriteLine(base64);
Console.WriteLine(Decrypt(base64, key));

Выход

Qh+XfnIuIdgllOiKFgzCpTURW+bUuj91S91zA1przRQ=
Hello world

Я думаю, этого достаточно, чтобы обеспечить безопасность на стороне БД. Кстати, вы можете сохранить методы генератора ключей, если вам это нужно.

PS old Rijndael solutionRijndael был заменен Aes в .NET я не помню точную причину, но что-то вроде Rijndael есть проблема с заполнением и Aes починил это.


Еще один совет. Если вы используете Windows и зашифрованные данные могут быть потеряны (в экстренных случаях), вы можете использовать DPAPI для защиты ключ (Пакет MS NuGet существует, доступны режимы защиты «Текущий пользователь / локальный компьютер») и сохранить защищенный ключ в БД в той же последовательности, что и IV. Затем вы можете использовать полностью случайный ключ для каждого Encrypt() вызовите и сохраните его с данными. Данные могут быть потеряны при переустановке Windows или перемещении сервера ASP.NET на другой компьютер, поскольку DPAPI использует ключ, связанный с учетными данными текущего пользователя или идентификатором локального компьютера. Но защищенные данные не могут быть восстановлены, если БД была украдена или иным образом на другом ПК. В любом случае узнайте, как работает DPAPI, это может быть полезно, если сервер работает под Windows.

    Добавить комментарий

    Ваш адрес email не будет опубликован. Обязательные поля помечены *