.NET's AES does not implement CTR directly. It only implements CBC, CFB, CTS, ECB and OFB.
Can I use any of these modes and securely implement CTR around them, or do I need to use a different library altogether?
.NET's AES does not implement CTR directly. It only implements CBC, CFB, CTS, ECB and OFB.
Can I use any of these modes and securely implement CTR around them, or do I need to use a different library altogether?
A compact standalone implementation based on the code by @quadfinity.
(Despite naming of the class in the original code) It can work with any key size: 128, 192 and 256. Just provide a key of a correct size. salt must have 128 bits (16 bytes).
The method works both for encryption and decryption.
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(
"Salt size must be same as block size " +
$"(actual: {salt.Length}, expected: {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));
}
}
If you want to encrypt or decrypt a file, use File.OpenRead for inputStream and File.Create for the outputStream:
using (Stream inputStream = File.OpenRead("file.in"))
using (Stream outputStream = File.Create("file.out"))
{
AesCtrTransform(key, salt, inputStream, outputStream);
}
See also PowerShell version of the code.
Encrypt and Decrypt using the AES/CTR/NoPadding algorithm with an all-zero, 16-byte initialization vector (IV) and a single-use 256-bit AES decryption key using a cryptographically-secure generator.
Using AesCtrTransform method from @martin code, I have the below usage example. Note that I'm leaving the Initialization Vector (IV) byte array empty here but you should populate it if you want to make things more secure (Learn more: is it safe to reuse IV), but then you have to store the IV somewhere as well as the key.
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);