.NET的AES不直接实现CTR。它只实现CBC,CFB,CTS,ECB和OFB。
我是否可以使用这些模式中的任何一种并安全地围绕它们实施点击率,或者我是否需要完全使用其他库?
答案 0 :(得分:12)
是的,你可以在ECB模式下使用.NET的AES构建一个CTR,并为每个加密的块自己初始化和增加一个计数器。
这方面的一个例子是WinZipAes加密流,它是开源DotNetZip的一部分。
WinZip指定在加密的ZIP文件中使用AES加密,在CTR模式下使用AES。 DotNetZip使用ECB和计数器实现CTR模式。
有关评论,请参阅here。
答案 1 :(得分:10)
您需要做的就是在ECB模式下使用AES,使用密钥(无填充,无IV)来加密128位计数器。然后将纯文本与计数器的加密输出进行异或。对于每个块,计数器递增。由于XOR运算符的属性,加密和解密是相同的。
您可以在此处找到AES128点击率模式的实现(我自己的):
https://gist.github.com/hanswolff/8809275
它应该易于使用。
答案 2 :(得分:9)
基于code by @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));
}
}
答案 3 :(得分:5)
Bouncy Castle的对称加密实现似乎支持CTR:
答案 4 :(得分:1)
使用AES / CTR / NoPadding算法和16位全零初始化向量(IV)以及使用加密安全生成器的一次性256位AES解密密钥进行加密和解密。
使用@martin代码中的AesCtrTransform method,我有以下用法示例。 请注意,我在这里将初始化向量(IV)字节数组留空,但是如果您想使事情更安全(Learn more: is it safe to reuse 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);