.NET的AES并没有直接实现CTR。它仅实现 CBC、CFB、CTS、ECB 和 OFB。
我可以使用这些模式中的任何一种并围绕它们安全地实现点击率,还是我需要完全使用不同的库?
基于@quadfinity代码的紧凑独立实现。
(尽管在原始代码中对类进行了命名) 它可以使用任何密钥大小:128、192 和 256。只需提供正确大小的 key
。
salt
必须有 128 位(16 字节)。该方法适用于加密和解密。
public static void AesCtrTransform(
byte[] key, byte[] salt, Stream inputStream, Stream outputStream)
{
SymmetricAlgorithm aes = Aes.Create();
aes.Mode = CipherMode.ECB;
aes.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})");
}
var counter = (byte[])salt.Clone();
var 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));
}
}
如果要加密或解密文件,请使用 File.OpenRead
表示
inputStream
,使用
File.Create
表示
outputStream
:
using (Stream inputStream = File.OpenRead("file.in"))
using (Stream outputStream = File.Create("file.out"))
{
AesCtrTransform(key, salt, inputStream, outputStream);
}
PowerShell 版本的代码。
WinZipAes 加密流就是一个例子,它是开源 DotNetZip 的一部分。
WinZip 指定对加密的 ZIP 文件使用 AES 加密,在 CTR 模式下使用 AES。 DotNetZip 使用 ECB 和计数器实现 CTR 模式。
此处获取一些评论。
使用 @martin 代码中的
AesCtrTransform 方法,我有以下用法示例。 请注意,我在这里将初始化向量 (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);