.NET AES не реализует CTR напрямую. Он реализует только CBC, CFB, CTS, ECB и OFB.
Могу ли я использовать любой из этих режимов и безопасно применять CTR вокруг них, или мне нужно вообще использовать другую библиотеку?
.NET AES не реализует CTR напрямую. Он реализует только CBC, CFB, CTS, ECB и OFB.
Могу ли я использовать любой из этих режимов и безопасно применять CTR вокруг них, или мне нужно вообще использовать другую библиотеку?
Да, вы можете построить CTR с использованием .NET AES в режиме ECB и счетчика, который вы сами инициализируете и увеличиваете, для каждого блока, зашифрованного.
Примером этого является поток шифрования WinZipAes, который является частью DotNetZip с открытым исходным кодом.
WinZip определяет использование AES-шифрования для зашифрованных ZIP файлов, используя AES в режиме CTR. DotNetZip реализует режим CTR с использованием ECB и счетчика.
См. здесь для некоторых комментариев.
Все, что вам нужно сделать, это использовать AES в режиме ECB с ключом (без прокладки, нет IV) для шифрования 128-битного счетчика. Простой текст затем XORed с зашифрованным выходом счетчика. Для каждого блока счетчик увеличивается. Шифрование и дешифрование одинаковы из-за свойств оператора XOR.
Здесь вы можете найти реализацию (мой собственный) для режима CTR AES128:
https://gist.github.com/hanswolff/8809275
Он должен быть прост в использовании.
Компактная автономная реализация, основанная на коде @quadfinity.
(Несмотря на название класса в исходном коде). Он может работать с ключами любого размера: 128, 192 и 256. Просто предоставьте key
правильного размера. salt
должен иметь 128 бит (16 байтов).
Этот метод работает как для шифрования, так и для дешифрования.
public static void AesCtrTransform(
byte[] key, byte[] salt, Stream inputStream, Stream outputStream)
{
SymmetricAlgorithm aes =
new AesManaged { Mode = CipherMode.ECB, Padding = PaddingMode.None };
int blockSize = aes.BlockSize / 8;
if (salt.Length != blockSize)
{
throw new ArgumentException(
string.Format(
"Salt size must be same as block size (actual: {0}, expected: {1})",
salt.Length, blockSize));
}
byte[] counter = (byte[])salt.Clone();
Queue<byte> xorMask = new Queue<byte>();
var zeroIv = new byte[blockSize];
ICryptoTransform counterEncryptor = aes.CreateEncryptor(key, zeroIv);
int b;
while ((b = inputStream.ReadByte()) != -1)
{
if (xorMask.Count == 0)
{
var counterModeBlock = new byte[blockSize];
counterEncryptor.TransformBlock(
counter, 0, counter.Length, counterModeBlock, 0);
for (var i2 = counter.Length - 1; i2 >= 0; i2--)
{
if (++counter[i2] != 0)
{
break;
}
}
foreach (var b2 in counterModeBlock)
{
xorMask.Enqueue(b2);
}
}
var mask = xorMask.Dequeue();
outputStream.WriteByte((byte)(((byte)b) ^ mask));
}
}
См. также версию кода PowerShell.
Функция симметричного шифрования Bouncy Castle, похоже, поддерживает CTR:
Зашифруйте и расшифруйте, используя алгоритм AES/CTR/NoPadding с 16-байтовым вектором инициализации (IV), состоящим из всех нулей, и одноразовым 256-битным ключом дешифрования AES, используя криптографически безопасный генератор.
Используя метод AesCtrTransform из кода @martin, у меня есть пример использования ниже. Обратите внимание, что я оставляю здесь байтовый массив вектора инициализации (IV) пустым, но вы должны заполнить его, если хотите повысить безопасность (Подробнее: безопасно ли повторно использовать IV), но тогда вы где-то должны хранить IV и ключ.
const string text = "Hello world";
var key = new byte[32];
var initializationVector = new byte[16];
using (var random = new RNGCryptoServiceProvider())
{
random.GetNonZeroBytes(key);
}
string output;
string outputEncrypted;
using (var outputEncryptedStream = new MemoryStream())
{
using (var inputStream = new MemoryStream(Encoding.UTF8.GetBytes(text)))
{
AesCtrTransform(key, initializationVector, inputStream, outputEncryptedStream);
}
outputEncryptedStream.Position = 0;
using (var reader = new StreamReader(outputEncryptedStream, Encoding.UTF8, true, 1024, true))
{
outputEncrypted = reader.ReadToEnd();
}
outputEncryptedStream.Position = 0;
using (var outputDecryptedStream = new MemoryStream())
{
AesCtrTransform(key, initializationVector, outputEncryptedStream, outputDecryptedStream);
outputDecryptedStream.Position = 0;
using (var reader = new StreamReader(outputDecryptedStream))
{
output = reader.ReadToEnd();
}
}
}
Assert.IsTrue(!string.IsNullOrEmpty(outputEncrypted));
Assert.AreEqual(text, output);