Рейтинг:0

Шифрование AES-GCM в .NET Core

флаг de

Я создал службу шифрования, используя AES-GCM, чтобы зашифровать конфиденциальные данные в базе данных. Во-первых, я генерирую криптографический ключ из пароля (вероятно, он будет храниться в Kubernetes Secrets) с помощью Rfc2898DeriveBytes. Затем передать этот ключ экземпляру AesGcm.Вы можете найти реализацию ниже.

открытый класс CryptoService: ICryptoService, IDisposable
{
    частный только для чтения AesGcm _aesGcm;

    общедоступный CryptoService (строковый пароль, строковая соль)
    {
        ключ байта [] = новый Rfc2898DeriveBytes (пароль, Encoding.UTF8.GetBytes (соль), 200000, HashAlgorithmName.SHA512).GetBytes (32);
        
        // Получает безопасно сгенерированный случайным образом зашифрованный ключ шифрования данных из хранилища Azure.
        строка зашифрованнаяEncryptionKey = AzureVault.GetDataEncryptionKey();
        byte[] EncryptionKey = AzureVault.Decrypt(encryptedEncryptionKey, ключ);

        _aesGcm = новый AesGcm (Ключ шифрования);
    }

    общедоступная строка Шифрование (строка простого текста)
    {
        byte[] plainBytes = Encoding.UTF8.GetBytes(plainText);

        int nonceSize = AesGcm.NonceByteSizes.MaxSize;
        int tagSize = AesGcm.TagByteSizes.MaxSize;
        int cipherSize = plainBytes.Length;

        // Объединяем для упрощения кодирования
        intcryptedDataLength = 4 + nonceSize + 4 + tagSize + cipherSize;
        Span<байт> зашифрованные данные = длина зашифрованных данных < 1024 ? stackalloc byte[encryptedDataLength] : новый байт[encryptedDataLength].AsSpan();


        BinaryPrimitives.WriteInt32LittleEndian (encryptedData.Slice (0, 4), nonceSize);
        BinaryPrimitives.WriteInt32LittleEndian(encryptedData.Slice(4 + nonceSize, 4), tagSize);
        var nonce = зашифрованные данные.Slice(4, nonceSize);
        var tag = зашифрованные данные.Slice(4 + nonceSize + 4, tagSize);
        var cipherBytes = зашифрованные данные.Slice(4 + nonceSize + 4 + tagSize, cipherSize);

        RandomNumberGenerator.Fill(одноразовый);

        _aesGcm.Encrypt(nonce, plainBytes.AsSpan(), cipherBytes, тег);

        вернуть Convert.ToBase64String (зашифрованные данные);
    }   

    общедоступная строка Расшифровать (строка cipherText)
    {
        Span<byte> зашифрованные данные = Convert.FromBase64String(cipherText).AsSpan();

        int nonceSize = BinaryPrimitives.ReadInt32LittleEndian (encryptedData.Slice (0, 4));
        int tagSize = BinaryPrimitives.ReadInt32LittleEndian (encryptedData.Slice (4 + nonceSize, 4));
        int cipherSize = зашифрованные данные. длина - 4 - nonceSize - 4 - tagSize;

        var nonce = зашифрованные данные.Slice(4, nonceSize);
        var tag = зашифрованные данные.Slice(4 + nonceSize + 4, tagSize);
        var cipherBytes = зашифрованные данные.Slice(4 + nonceSize + 4 + tagSize, cipherSize);

        Span<byte> plainBytes = cipherSize < 1024? stackalloc byte[cipherSize] : новый байт[cipherSize];
        _aesGcm.Decrypt(nonce, cipherBytes, tag, plainBytes);

        вернуть Encoding.UTF8.GetString(plainBytes);
    }    
}

Вот мой вопрос. Мне интересно, достаточно ли безопасна эта реализация, поскольку я не эксперт в области безопасности. Я упустил какую-то точку или дыру в безопасности, кроме защиты паролем? Любые советы и предложения будут с благодарностью.

Спасибо.

Рейтинг:0
флаг cn

Это не обзор кода, но есть некоторые особенности, характерные для криптографии:

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

Главное, что я вижу, это то, что вы шифруете данные напрямую с помощью пароля, я бы предложил вместо этого сгенерировать безопасный случайный ключ (DEK - ключ шифрования данных), а затем использовать пароль для создания ключа для шифрования этого ключа (KEK - ключевое шифрование). ключ). Это позволяет вам изменить пароль без повторного шифрования всех данных и дает размер комбинации ключ + одноразовый номер 352 бита.

Другое дело, что если вам нужна 256-битная безопасность для AES, ваш пароль должен быть получен с использованием SHA512.

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

lagrance avatar
флаг de
Спасибо @Richie Frame. Это отличные предложения. Чтобы прояснить эти шаги: я сгенерирую криптографически безопасные случайные данные (32 байта?) для DEK. Затем зашифруйте DEK с помощью ключа (KEK), сгенерированного Rfc2898DeriveBytes, и сохраните зашифрованный DEK. Когда я хочу зашифровать/расшифровать данные, сначала я расшифровываю зашифрованный DEK с помощью KEK. В конце концов, я должен хранить как пароль (для KEK), так и зашифрованный DEK.
Richie Frame avatar
флаг cn
Довольно близко, вы должны хранить соль для функции получения ключа на основе пароля (есть ли она в коде?) и помнить пароль или хранить его безопасно вне контекста данных. Большой двоичный объект зашифрованных данных будет состоять из соли pbkdf, зашифрованного DEK, одноразового номера, тега, зашифрованного текста и любых незашифрованных метаданных.
lagrance avatar
флаг de
Спасибо за все предложения @Richie Frame. Я обновил фрагмент кода, чтобы дать понять другим людям. Я решил использовать Key Vault для своего DEK.
Richie Frame avatar
флаг cn
@lagrance вы также можете явно указать размеры одноразовых номеров и тегов, вместо того, чтобы просто получать максимальное значение из класса, это не имеет значения для тега, однако когда-нибудь они могут решить поддерживать 128-битные одноразовые номера, тогда все код ломается
lagrance avatar
флаг de
Маловероятно, что это произойдет, но вы правы, я должен установить их самостоятельно для надежного кода.

Ответить или комментировать

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