I created a crypto service using AES-GCM in order to encrypt the sensitive data in database. Firstly, I'm generating a cryptographic key from a password (probably will be stored in Kubernetes Secrets) by using Rfc2898DeriveBytes. Then passing this key to AesGcm instance. You can find the implementation down below.
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);
}
}
Here is my question. I am wondering that if this implementation is secure enough since I am not an expert in security. Am I missing a point or security hole except password security? Any advice and suggestions would be greatly appreciated.
Thanks.