using System.Security.Cryptography; using System.Text; using DXPBLL.Auth; namespace DXPBLL.Kyc; public class DXPKycEncryption : IDXPKycEncryption { private const int NonceSize = 12; private const int TagSize = 16; private readonly IDXPSecretResolver _SecretResolver; public DXPKycEncryption(IDXPSecretResolver secretResolver) { _SecretResolver = secretResolver; } public async Task EncryptAsync(string plaintext, CancellationToken ct) { var key = await GetKeyBytesAsync(ct).ConfigureAwait(false); var nonce = RandomNumberGenerator.GetBytes(NonceSize); var plainBytes = Encoding.UTF8.GetBytes(plaintext); var cipherBytes = new byte[plainBytes.Length]; var tag = new byte[TagSize]; using var aesGcm = new AesGcm(key, TagSize); aesGcm.Encrypt(nonce, plainBytes, cipherBytes, tag); // nonce || tag || ciphertext, base64 var combined = new byte[NonceSize + TagSize + cipherBytes.Length]; Buffer.BlockCopy(nonce, 0, combined, 0, NonceSize); Buffer.BlockCopy(tag, 0, combined, NonceSize, TagSize); Buffer.BlockCopy(cipherBytes, 0, combined, NonceSize + TagSize, cipherBytes.Length); return Convert.ToBase64String(combined); } public async Task DecryptAsync(string ciphertext, CancellationToken ct) { var key = await GetKeyBytesAsync(ct).ConfigureAwait(false); var combined = Convert.FromBase64String(ciphertext); var nonce = combined[..NonceSize]; var tag = combined[NonceSize..(NonceSize + TagSize)]; var cipherBytes = combined[(NonceSize + TagSize)..]; var plainBytes = new byte[cipherBytes.Length]; using var aesGcm = new AesGcm(key, TagSize); aesGcm.Decrypt(nonce, cipherBytes, tag, plainBytes); return Encoding.UTF8.GetString(plainBytes); } public string Hash(string normalizedValue) { var bytes = SHA256.HashData(Encoding.UTF8.GetBytes(normalizedValue)); return Convert.ToHexString(bytes); } private async Task GetKeyBytesAsync(CancellationToken ct) { var keyString = await _SecretResolver.GetKycEncryptionKeyAsync(ct).ConfigureAwait(false); return SHA256.HashData(Encoding.UTF8.GetBytes(keyString)); // derive 32-byte AES-256 key } }