开发者

Can I use AES in CTR mode in .NET?

开发者 https://www.devze.com 2023-03-13 21:58 出处:网络
.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 secure开发者_Go百科ly implement CTR around them, or do I need to use a diff

.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 secure开发者_Go百科ly implement CTR around them, or do I need to use a different library altogether?


Yes, you can build a CTR using .NET's AES in ECB mode and a counter, that you yourself initialize and increment, for each block encrypted.

An example of this is the WinZipAes encryption stream, which is part of the open-source DotNetZip.
WinZip specifies the use of AES encryption for encrypted ZIP files, using AES in CTR mode. DotNetZip implements the CTR mode using ECB and the counter.

See here for some comments.


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.


All you need to do is to use AES in ECB mode with a key (no padding, no IV) to encrypt a 128-bit counter. The plain text is then XORed with the encrypted output of the counter. For each block the counter is incremented. Encryption and decryption is the same due to the properties of the XOR operator.

You can find an implementation (my own) for AES128 CTR mode here:

https://gist.github.com/hanswolff/8809275

It should be easy to use.


Bouncy Castle's symmetric encryption implementation seems to support CTR:

  • Symmetric key algorithms: AES, Blowfish, Camellia, CAST5, CAST6, DESede, DES, GOST28147, HC-128, HC-256, IDEA, NaccacheStern, RC2, RC4, RC5-32, RC5-64, RC6, Rijndael, Serpent, Skipjack, TEA/XTEA, Twofish, and VMPC.
  • Symmetric key modes: CBC, CFB, CTS, GOFB, OFB, OpenPGPCFB, and SIC (aka CTR).

http://www.bouncycastle.org/csharp/


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);
0

精彩评论

暂无评论...
验证码 换一张
取 消