День 1091. #ЗаметкиНаПолях
Упрощённые API Криптографии в .NET5/6
За последние несколько выпусков в .NET улучшили сценарии использования разработчиками криптографических примитивов, таких как AES, SHA и т. д.
«Лучше» — интересное понятие, когда мы говорим о дизайне криптографического API. Для разработчика «лучше» может означать большую пропускную способность, меньшее количество аллокаций или просто менее громоздкий API. Для автора фреймворка или библиотеки это означает думать о том, как разработчики могут использовать или неправильно использовать API.
Давайте посмотрим на шифрование AES, каким оно было во времена .NET Framework:
byte[] data = …;
using (Aes aes = Aes.Create())
{
byte[] key = …;
byte[] iv = …;
using ICryptoTransform transform = aes.CreateEncryptor(key, iv);
byte[] encrypted = transform.TransformFinalBlock(data, 0, data.Length);
}
Тут от силы десяток строк кода, но не все из них очевидны. Что такое transform? Что за финальный блок?
API предоставляют довольно много функций и имеют сбивающие с толку названия. Например, TransformFinalBlock несмотря на то, что в его названии есть «Block», почти всегда будет способен шифровать более одного блока. Это также означает, что данные не нужно выравнивать по блокам. Поскольку ничего из этого невозможно понять, разработчики часто обходят предполагаемые проблемы, например, обрабатывают отдельные блоки. Хотя этот дизайн API предлагает наибольшую гибкость для разработчиков, он также предлагает наибольшую сложность.
Простые API важны для защиты от неправильного использования, и .NET стал лучше в этом отношении за последние несколько выпусков, предложив для Aes, TripleDES, и т.п. алгоритмов API с вызовом единственного метода, вроде EncryptCbc, EncryptEcb или DecryptCbc и DecryptEcb:
byte[] data = …;
using (Aes aes = Aes.Create())
{
byte[] key = …;
byte[] iv = …;
aes.Key = key;
// Шифруем все данные за раз
byte[] encrypted = aes.EncryptCbc(data, iv);
}
Здесь нет ICryptoTransform и нет необходимости беспокоиться о блоках, отступах и т. д.
Кроме того, некоторые методы сделали статическими. Хеширование до .NET 5 это выглядело примерно так:
byte[] data = …;
using (SHA256 hash = SHA256.Create())
{
byte[] digest = hash.ComputeHash(data);
}
Теперь вместо получения экземпляра алгоритма, есть статический метод:
byte[] digest = SHA256.HashData(data);
Для .NET 6 API хеширования также были перенесены в классы HMAC, предлагая такие же улучшенные API и более высокую производительность. PBKDF2 также улучшил производительность в .NET 6:
byte[] salt = RandomNumberGenerator.GetBytes(32);
byte[] prk = Rfc2898DeriveBytes.Pbkdf2(
userPassword,
salt,
iterations: 200_000,
HashAlgorithmName.SHA256,
outputLength: 32);
Обновлённые API позволяют работать с ReadOnlySpan<byte> для входных данных и возможность записи в Span<byte> для выходных данных.
Источник: https://vcsjones.dev/one-shot-crypto/