如何使用 .net 核心 2 正确实现 AES 加密?



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

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

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

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

然后我们解密密钥,如果它与数据库中的特定值匹配(再次,取决于客户端(,我们继续执行其他一些操作。

因此,我决定使用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;
}
}

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

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

在萨特图普注册.cs

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

我有几个关于所有这些的问题。

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

感谢您的阅读!

编辑:

你说"我们是生成加密密钥的人,我们传递它 客户端的加密密钥" - 如何安全地发生?

-> 客户必须连接到他的帐户,在那里他可以访问该encKey

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

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

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

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

解决此问题的最佳方法是处理原语的本质 - 不足以单独使用的原语。还有其他库,如libsodium,它们是更抽象的密码学概念,并提供"做正确的事情"的简单API。

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

除非使用像libsodium这样的东西,否则有一些问题需要解决。

  1. 密文没有身份验证(密码学中的"身份验证"有其自身的含义,不像登录身份验证(。

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

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

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

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

如何生成密钥和 IV?有工具吗?

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

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

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

最新更新