Hoc*_*eyJ 2 c# encryption aes encryption-symmetric aes-gcm
读到 RijndaelManaged 已被弃用并且AesGcm(在 .NET Core 3.1 中引入)优于AesManaged后,我尝试使用本教程和此答案来实现AesGcm。
这是我的代码:
/// Perform AES Encryption, returning the result as a byte array.
/// </summary>
/// <param name="bytesToEncrypt">string, file or data represented as byte array</param>
/// <param name="passwordBytes">A unique password for the encryption (must be 32 bytes?)</param>
/// <returns>The data encrypted</returns>
public byte[] EncryptData(byte[] bytesToEncrypt, byte[] passwordBytes)
{
// Based on /sf/ask/4262254181/#60891115
// Get parameter sizes
int nonceSize = AesGcm.NonceByteSizes.MaxSize;
int tagSize = AesGcm.TagByteSizes.MaxSize;
int cipherSize = bytesToEncrypt.Length;
// We write everything into one big array for easier encoding
int encryptedDataLength = 4 + nonceSize + 4 + tagSize + cipherSize;
Span<byte> encryptedData = encryptedDataLength < 1024
? stackalloc byte[encryptedDataLength]
: new byte[encryptedDataLength].AsSpan();
// Copy parameters
BinaryPrimitives.WriteInt32LittleEndian(encryptedData.Slice(0, 4), nonceSize);
BinaryPrimitives.WriteInt32LittleEndian(encryptedData.Slice(4 + nonceSize, 4), tagSize);
var nonce = encryptedData.Slice(4, nonceSize);
var tag = encryptedData.Slice(4 + nonceSize + 4, tagSize);
var cipherBytes = encryptedData.Slice(4 + nonceSize + 4 + tagSize, cipherSize);
// Generate secure nonce
RandomNumberGenerator.Fill(nonce);
// Encrypt
using (var aes = new AesGcm(passwordBytes))
{
aes.Encrypt(nonce, bytesToEncrypt.AsSpan(), cipherBytes, tag);
}
return encryptedData.ToArray();
}
/// <summary>
/// Takes in an AES encrypted byte array, decrypts it and returns the resulting unencrypted byte array.
/// </summary>
/// <param name="encryptedBytes">A string, file or object represented as a byte array that's previously been encrypted.</param>
/// <param name="passwordBytes">The password used to encrypt the data. </param>
/// <returns></returns>
public byte[] DecryptData(byte[] encryptedBytes, byte[] passwordBytes)
{
// Decode
Span<byte> encryptedData = encryptedBytes.AsSpan();
// Extract parameter sizes
int nonceSize = BinaryPrimitives.ReadInt32LittleEndian(encryptedData.Slice(0, 4));
int tagSize = BinaryPrimitives.ReadInt32LittleEndian(encryptedData.Slice(4 + nonceSize, 4));
int cipherSize = encryptedData.Length - 4 - nonceSize - 4 - tagSize;
// Extract parameters
var nonce = encryptedData.Slice(4, nonceSize);
var tag = encryptedData.Slice(4 + nonceSize + 4, tagSize);
var cipherBytes = encryptedData.Slice(4 + nonceSize + 4 + tagSize, cipherSize);
// Decrypt
Span<byte> plainBytes = cipherSize < 1024
? stackalloc byte[cipherSize]
: new byte[cipherSize];
using (var aes = new AesGcm(passwordBytes))
{
aes.Decrypt(nonce, cipherBytes, tag, plainBytes);
}
// Convert plain bytes back into string
return plainBytes.ToArray();
}
Run Code Online (Sandbox Code Playgroud)
我注意到的一件事是,似乎没有迭代的地方。
例如,在 AesManaged 中,我总是像下面这样进行迭代,因为迭代使攻击变得更加复杂。我遵循类似的密码哈希模式:
// Set Symmetric encryption algorithm
// Based on http://stackoverflow.com/questions/27645527/aes-encryption-on-large-files
var AES = Aes.Create("AesManaged");
AES.KeySize = 256;
AES.BlockSize = 128;
AES.Padding = PaddingMode.PKCS7;
//http://stackoverflow.com/questions/2659214/why-do-i-need-to-use-the-rfc2898derivebytes-class-in-net-instead-of-directly
//"What it does is repeatedly hash the user password along with the salt." High iteration counts.
var key = new Rfc2898DeriveBytes(passwordBytes, salt, 100000);
AES.Key = key.GetBytes(AES.KeySize / 8);
AES.IV = key.GetBytes(AES.BlockSize / 8);
...
Run Code Online (Sandbox Code Playgroud)
我很感激这nonce会产生影响,但我之前所做的一切都依赖于多次迭代,所以不在AesGcm.
AesGcm 似乎没有迭代的方法。我应该在某个地方迭代某个部分吗?如果我应该迭代,我该怎么做?
读完后
RijndaelManaged,我正在尝试使用本教程和这个答案AesGcm来实现。AesManagedAesGcm
RijndaelManaged已弃用,但它是一个实现类。您应该已经使用Aes.Create它很长时间了,更喜欢无参构造函数。一般来说,这将为您提供优化的 C/C++ 版本,该版本可以利用 AES_NI 指令集,该指令集很可能是您 CPU 的一部分。“托管”类是使用 .NET 实现的,速度相当慢。
AesGcm看起来可以而且应该直接使用,并增加密码的真实性和完整性。
Run Code Online (Sandbox Code Playgroud)using var aes = new AesGcm(passwordBytes);
是的,不,那是不对的。密码不是密钥,AES 对密钥而不是密码进行操作。因此,从密码导出密钥(可能还有 IV)的部分丢失了。这应该是从您提到的前面的示例中复制的。但是,您应该使用int nonceSize = AesGcm.NonceByteSizes.MaxSizeGCM,因为 GCM 最适合 96 位/12 字节随机数(也就是说,我想这会给您带来什么,原则上 GCM 没有随机数的功能最大大小)。
当然,如果性能允许,你可以增加迭代次数。计算随机数就可以了,只要您按应有的方式更改每个新密文的盐即可。请注意,如果盐和随机数重复,您的方案将严重失败,因此请使用 128 位/16 字节盐以确保安全。
AesGcm 似乎没有迭代的方法。我应该在某个地方迭代某个部分吗?如果我应该迭代,我该怎么做?
这是正确的,但那是因为它假设一个完全随机的密钥。如果您有密码,则必须执行 PBKDF2(名称错误的Rfc2898DeriveBytes类实现的)或其他基于密码的密钥派生函数。
请注意,AES-GCM 的上限为 64 GiB(减去几个字节)。
更糟糕的是,它要求您将所有明文/密文存储在缓冲区中。从这个意义上说,它可能不是基于文件的加密的最佳选择。您也可以使用 GCM 加密块,但随后您需要对身份验证标签执行 HMAC,以确保单独身份验证的块不会重新排序。
AES-CBC + HMAC over IV 和密文可能是另一种选择。
| 归档时间: |
|
| 查看次数: |
661 次 |
| 最近记录: |