Encryption

.NET Core 中的 AES-GCM 加密

  • September 28, 2021

我使用 AES-GCM 創建了一個加密服務,以加密數據庫中的敏感數據。首先,我使用 Rfc2898DeriveBytes 從密碼(可能會儲存在 Kubernetes Secrets 中)生成加密密鑰。然後將此密鑰傳遞給 AesGcm 實例。您可以在下面找到實現。

public class CryptoService : ICryptoService, IDisposable
{
   private readonly AesGcm _aesGcm;

   public CryptoService(string password, string salt)
   {
       byte[] key = new Rfc2898DeriveBytes(password, Encoding.UTF8.GetBytes(salt), 200000, HashAlgorithmName.SHA512).GetBytes(32);
       
       //Gets securely random generated encrypted data encryption key from Azure Vault.
       string encryptedEncryptionKey = AzureVault.GetDataEncryptionKey();
       byte[] encryptionKey = AzureVault.Decrypt(encryptedEncryptionKey, key);

       _aesGcm = new AesGcm(encryptionKey);
   }

   public string Encrypt(string plainText)
   {
       byte[] plainBytes = Encoding.UTF8.GetBytes(plainText);

       int nonceSize = AesGcm.NonceByteSizes.MaxSize;
       int tagSize = AesGcm.TagByteSizes.MaxSize;
       int cipherSize = plainBytes.Length;

       // Combine for easier encoding
       int encryptedDataLength = 4 + nonceSize + 4 + tagSize + cipherSize;
       Span<byte> encryptedData = encryptedDataLength < 1024 ? stackalloc byte[encryptedDataLength] : new byte[encryptedDataLength].AsSpan();


       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);

       RandomNumberGenerator.Fill(nonce);

       _aesGcm.Encrypt(nonce, plainBytes.AsSpan(), cipherBytes, tag);

       return Convert.ToBase64String(encryptedData);
   }   

   public string Decrypt(string cipherText)
   {
       Span<byte> encryptedData = Convert.FromBase64String(cipherText).AsSpan();

       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;

       var nonce = encryptedData.Slice(4, nonceSize);
       var tag = encryptedData.Slice(4 + nonceSize + 4, tagSize);
       var cipherBytes = encryptedData.Slice(4 + nonceSize + 4 + tagSize, cipherSize);

       Span<byte> plainBytes = cipherSize < 1024 ? stackalloc byte[cipherSize] : new byte[cipherSize];
       _aesGcm.Decrypt(nonce, cipherBytes, tag, plainBytes);

       return Encoding.UTF8.GetString(plainBytes);
   }    
}

這是我的問題。我想知道這個實現是否足夠安全,因為我不是安全專家。除了密碼安全之外,我是否遺漏了一點或安全漏洞?任何意見和建議將不勝感激。

謝謝。

這不是codereview,但有一些特定於加密的東西:

根據您的威脅模型,將源數據轉換為字節數組而不是從記憶體中讀取其內容可能會有問題,尤其是在它足夠大的情況下。

我看到的主要內容是您直接使用密碼加密數據,我建議改為生成安全隨機密鑰(DEK - 數據加密密鑰),然後使用密碼生成密鑰來加密該密鑰(KEK - 密鑰加密鑰匙)。這允許您在不重新加密所有數據的情況下更改密碼,並提供 352 位的密鑰+隨機數組合大小。

另一件事是,如果您想要 AES 的 256 位安全性,您的密碼密鑰派生應該使用 SHA512。

您還需要使用任何可用的程式方法來防止密鑰材料被分頁到磁碟。並考慮當您提供錯誤的解密密碼時會發生什麼。

引用自:https://crypto.stackexchange.com/questions/95240