c# - Linq选择列表解密非常慢

标签 c# sql-server asp.net-mvc entity-framework encryption

我的应用程序是使用 EF 6.2 的 MVC 5。我在生成列表时解密某些列,它可以工作但速度很慢。有没有更好的方法来提高这种方法的性能?

 var mylist = await _db.vw_LearnerCourse.AsNoTracking().ToListAsync();
        var grid1 = mylist.Select(c => new
        {
            FirstName = Encryption.Decrypt5(c.FirstName),
            LastName = Encryption.Decrypt5(c.LastName)
        }).ToList();

   public static string Decrypt5(string cipherText)
    {
        if (string.IsNullOrWhiteSpace(cipherText)) return null;
        if (!string.IsNullOrWhiteSpace(cipherText))
        {
            string strOut;
            try
            {
                var arrOffsets = new ArrayList();
                arrOffsets.Insert(0, 73);
                arrOffsets.Insert(1, 56);
                arrOffsets.Insert(2, 31);
                arrOffsets.Insert(3, 58);
                arrOffsets.Insert(4, 77);
                arrOffsets.Insert(5, 75);
                strOut = "";
                int intCounter;
                for (intCounter = 0;
                    intCounter <= cipherText.Length - 1;
                    intCounter += 2)
                {
                    var strSub = cipherText.Substring(intCounter, 1);
                    var strSub1 = cipherText.Substring(intCounter + 1, 1);
                    var intVal = int.Parse(strSub,
                                     NumberStyles.HexNumber) * 16 + int.Parse(strSub1,
                                     NumberStyles.HexNumber);
                    var intMod = intCounter / 2 % arrOffsets.Count;
                    var intNewVal = intVal -
                                    Convert.ToInt32(arrOffsets[intMod]) + 256;
                    intNewVal = intNewVal % 256;
                    var strDecimal = ((char)intNewVal).ToString();
                    strOut = strOut + strDecimal;
                }
            }
            catch (Exception err)
            {
                throw new Exception(err.Message);
            }
            var encryptionKey = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
            cipherText = strOut;
            cipherText = cipherText.Replace(" ", "+");
            var cipherBytes = Convert.FromBase64String(cipherText);
            using (var encryptor = Aes.Create())
            {
                var pdb = new Rfc2898DeriveBytes(encryptionKey, new byte[]
                {
                    xxxxxxx
                });
                encryptor.Key = pdb.GetBytes(32);
                encryptor.IV = pdb.GetBytes(16);
                using (var ms = new MemoryStream())
                {
                    using (var cs = new CryptoStream(ms, encryptor.CreateDecryptor(),
                        CryptoStreamMode.Write))
                    {
                        cs.Write(cipherBytes, 0, cipherBytes.Length);
                        cs.Close();
                    }
                    cipherText = Encoding.Unicode.GetString(ms.ToArray());
                }
            }
            return cipherText;
        }
        return null;
    }

最佳答案

性能不佳的主要原因是每次加密时都执行 PBKDF2 key 派生(通过 Rfc2898DeriveBytes)。 key 派生故意减慢以减缓攻击者的速度。 IE。每个加密都会增加人为的延迟,从而导致运行时观察到的性能损失。

PBKDF2 的执行时间可以通过迭代计数来调整。一般来说,该值设置得尽可能高,同时保持可接受的性能。典型值为 10,000 次迭代,请参见here .
发布的代码使用默认值 1000 次迭代(参见 here ),这已经非常低了。降低该值也会降低安全性,因此不建议这样做。
除此之外,改变迭代次数会导致与已有数据不兼容,因此需要进行数据迁移。

提高性能的另一种替代方法是在列表级别执行 key 派生(而不是对列表的每次加密)并将 key 和 IV 传递给 Decrypt5() 方法。这也确保了与旧数据的兼容性不会丢失:

// Key/IV derivation
var encryptionKey = "This is my passphrase";
var salt = new byte[] {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07};
var pdb = new Rfc2898DeriveBytes(encryptionKey, salt);
byte[] key = pdb.GetBytes(32);
byte[] iv = pdb.GetBytes(16);

// Decryption, key/IV are passed
var mylist = await _db.vw_LearnerCourse.AsNoTracking().ToListAsync();
var grid1 = mylist.Select(c => new
{
    FirstName = Encryption.Decrypt5(c.FirstName, key, iv),
    LastName = Encryption.Decrypt5(c.LastName, key, iv)
}).ToList();

...

// Decryption without key/IV derivation 
public static string Decrypt5(string cipherText, byte[] key, byte[] iv)
{
...
    var cipherBytes = Convert.FromBase64String(cipherText);
    using (var encryptor = Aes.Create())
    {
        encryptor.Key = key;
        encryptor.IV = iv;
        using (var ms = new MemoryStream())
        {
            using (var cs = new CryptoStream(ms, encryptor.CreateDecryptor(), CryptoStreamMode.Write))
            {
                cs.Write(cipherBytes, 0, cipherBytes.Length);
                cs.Close();
            }
            cipherText = Encoding.Unicode.GetString(ms.ToArray());
        }
    }
    return cipherText;
...
}

此更改显着提高了性能,同时保持了与现有数据的兼容性。


安全性:

当前代码的一个漏洞是静态盐。静态盐通常是不安全的,因为如果攻击成功,整个数据都会受到影响。由于在当前情况下, key 导出除了 key 之外还额外导出了 IV,因此不可避免的 key /IV 重用会进一步加剧这种情况。

为了提高安全性,应该消除这些漏洞。但不可避免地会失去与旧数据的兼容性,因此数据迁移是必要的。

为了提高安全性,应该为每个加密生成随机盐,而不是静态盐,或者至少在每个列表的性能方面生成随机盐。
为了避免在列表中重复使用 key /IV 对,不应通过 key 派生来派生 IV,而应为每次加密生成一个新的随机 IV。尽管随机 IV 的生成对性能有负面影响,但由于 key 派生的转变而带来的增益明显抵消了这一影响。

Salt 和 IV 不是 secret 的,通常与密文连接:salt|IV|ciphertext。在解密过程中,根据盐和 IV 的已知长度将这些部分分开。由于此处对列表的所有加密使用相同的盐,因此盐也可以单独存储。

对于迭代计数,不应使用默认值(1000 次迭代),而应使用性能可接受的最大可能值。

应避免对密码进行硬编码(否则,例如访问代码就足以危及密码),而应在运行时输入密码。

关于c# - Linq选择列表解密非常慢,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/73107186/

相关文章:

c# - 在方法上使用 await Task.Factory.StartNew 将立即返回

SQL 区分大小写的字符串比较

asp.net-mvc - 如何在 ASP.NET MVC 2 中的每个 Controller 操作之前执行某些代码?

asp.net-mvc - redirectToAction 导致空模型

C# - 空检查方法

c# - Entity Framework 5 中的 ExecuteStoreCommand 方法在哪里?

php - 存储过程不返回数据

c# - ASP.NET MVC 模型绑定(bind)到列表中

c# - 按仅在运行时已知的类型进行动态调度

SQL Server 动态排序依据