【问题标题】:How to properly implement AES encryption with .net core 2?如何使用 .net core 2 正确实施 AES 加密?
【发布时间】:2022-04-04 02:27:26
【问题描述】:

出于某种原因,我被要求实施一些基本加密来保护传输到客户端的特定值(取决于客户端)。

上下文是:我们是生成加密密钥的人,我们将该加密密钥传递给客户端,客户端永远不必解密它(但我们必须在后端)

示例 => 我们将加密密钥“123ABCDE==”提供给客户端。客户端调用我们的 API 传递数据 + 加密密钥,例如:

{
"encKey": "123ABCDE==",
"payload": "somedatahere"
}

然后我们解密密钥,如果它与 DB 中的特定值匹配(同样,取决于客户端),我们继续进行一些其他操作。

所以,我决定使用 AES 加密。以下是我现在所拥有的。

关键信息的定义:

public class KeyInfo
{
    public byte[] Key { get; }
    public byte[] Iv { get; }
    
    public KeyInfo()
    {
        using (var myAes = Aes.Create())
        {
            Key = myAes.Key;
            Iv = myAes.IV;
        }
    }

    public KeyInfo(string key, string iv)
    {
        Key = Convert.FromBase64String(key);
        Iv = Convert.FromBase64String(iv);
    }
}

加密方式

 private static byte[] Encrypt_AES(string plainText, byte[] key, byte[] iv)
        {
            if (plainText == null || plainText.Length <= 0)
                throw new ArgumentNullException(nameof(plainText));
            if (key == null || key.Length <= 0)
                throw new ArgumentNullException(nameof(key));
            if (iv == null || iv.Length <= 0)
                throw new ArgumentNullException(nameof(iv));
            byte[] encrypted;

            using (var aesAlgo = Aes.Create())
            {
                aesAlgo.Key = key;
                aesAlgo.IV = iv;
               
                var encryptor = aesAlgo.CreateEncryptor(aesAlgo.Key, aesAlgo.IV);
                using (var msEncrypt = new MemoryStream())
                {
                    using (var csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
                    {
                        using (var swEncrypt = new StreamWriter(csEncrypt))
                        {
                            swEncrypt.Write(plainText);
                        }

                        encrypted = msEncrypt.ToArray();
                    }
                }

            }

            return encrypted;
        }

解密方法:

private static string Decrypt_AES(byte[] cipherText, byte[] key, byte[] iv)
        {
            if (cipherText == null || cipherText.Length <= 0)
                throw new ArgumentNullException(nameof(cipherText));
            if (key == null || key.Length <= 0)
                throw new ArgumentNullException(nameof(key));
            if (iv == null || iv.Length <= 0)
                throw new ArgumentNullException(nameof(iv));

            string plaintext;
            using (var aesAlgo = Aes.Create())
            {
                aesAlgo.Key = key;
                aesAlgo.IV = iv;

                var decryptor = aesAlgo.CreateDecryptor(aesAlgo.Key, aesAlgo.IV);
                using (var msDecrypt = new MemoryStream(cipherText))
                {
                    using (var csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read))
                    {
                        using (var srDecrypt = new StreamReader(csDecrypt))
                        {
                            plaintext = srDecrypt.ReadToEnd();
                        }
                    }
                }
            }

            return plaintext;
        }
    }

存储在 appsettings.json 中的密钥和 IV:

"EncryptionKey": "myEncryptionKeyHere",
"EncryptionInitialVector": "myInitialVectorHere",

在 Satrtup.cs 中的注册

services.AddTransient(ec => new EncryptionService(new KeyInfo(appSettings.EncryptionKey, appSettings.EncryptionInitialVector)));

关于这一切我有几个问题。

  • AES rh 是否适合我的需求?
  • 这里的实现方式是否正确?
  • 我应该将密钥和 IV 存储在哪里? (不确定 appsettings.json 是否正常)
  • 如何生成密钥和 IV?有相应的工具吗?

感谢阅读!

编辑:

您说“我们是生成加密密钥的人,我们通过 给客户端的加密密钥”——这是如何安全地进行的?

-> 客户必须连接到他的帐户才能访问encKey

所以,阅读@vcsjones 的答案,AES 可能不是在这里实施的正确方法。由于我不想将 IV 存储在数据库中,如果客户端丢失它,这意味着他必须使用另一个 IV 生成另一个密钥,并在所有应用程序中更改 encKey

非对称加密会更好吗?如果我理解正确,这意味着使用私钥加密值,将该加密值提供给客户端 + 公钥(每个客户端都相同?)

【问题讨论】:

    标签: security asp.net-core encryption aes


    【解决方案1】:

    AES 是满足我需求的正确选择吗?

    就其本身而言,不,它不是。 AES 是一种加密原语 - 一个构建块 - 而这些构建块本身通常没有用处。例如,对于 AES-CBC(您正在使用的模式),这目前容易受到 padding oracle attack 的攻击并且缺乏身份验证。当与提供身份验证的其他原语(如 HMAC)结合使用时,AES 可能是正确的选择。

    解决此问题的最佳方法是处理原语的本来面目——那些不足以单独使用的原语。还有其他库,例如 libsodium,它们是更抽象的密码学概念,并提供“做正确的事”的简单 API。

    您说“我们是生成加密密钥的人,我们将该加密密钥传递给客户端”——这如何安全地发生?

    除非使用 libsodium 之类的东西,否则还有一些问题需要解决。

    1. 密文没有认证(密码学中的“认证”有其自身的含义,不像登录认证)。

    2. 初始化向量不应与同一个键多次使用。您似乎正在使用固定的 IV。你加密的每一件事都应该使用它自己的随机 IV。 IV不是秘密。但是,它应该与第 1 点中的密文一起进行身份验证。

    我应该将密钥和 IV 存储在哪里?

    IV,因为每个密文(加密输出)应该有一个 1:1 的它们应该与密文一起存储。解密时,调用者需要再次提供 IV。

    钥匙是真正的秘密。安全地存储它们很重要。如果您将其存储在 appsettings.json 中,那么您的密钥的安全性与该 JSON 文件的安全性相同。云环境中更常见的方法是使用托管服务。 Azure 有 Azure Key Vault,AWS 有 KMS 和 Secret Manager。

    如何生成 Key 和 IV ?有工具吗?

    它们应该使用 CSPRNG 生成。例如,以编程方式执行此操作:

    byte[] iv = new byte[128 / 8];
    RandomNumberGenerator.Fill(iv);
    //iv now contains random bytes
    

    您可以执行相同的操作来生成密钥。

    【讨论】:

    • 感谢您的详细解答!因为我的回答有点长,所以我刚刚更新了原帖:)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-03-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-10-29
    • 2021-06-03
    相关资源
    最近更新 更多