begin: PHI Column Encryption
This commit is contained in:
@@ -0,0 +1,48 @@
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using Microsoft.AspNetCore.DataProtection;
|
||||
using Microsoft.Extensions.Options;
|
||||
|
||||
public class PhiEncryptionService : IPhiEncryptionService
|
||||
{
|
||||
private readonly IDataProtector _protector;
|
||||
private readonly byte[] _searchKey;
|
||||
|
||||
public PhiEncryptionService(
|
||||
IDataProtectionProvider provider,
|
||||
IOptions<PhiEncryptionOptions> options)
|
||||
{
|
||||
var opts = options.Value;
|
||||
_protector = provider.CreateProtector(opts.ProtectorPurpose);
|
||||
_searchKey = Convert.FromBase64String(
|
||||
Convert.ToBase64String(Encoding.UTF8.GetBytes(opts.SearchTokenKey))[..44]); // normalize dev key
|
||||
}
|
||||
|
||||
public string Encrypt(string plaintext)
|
||||
{
|
||||
if (string.IsNullOrEmpty(plaintext))
|
||||
return plaintext;
|
||||
return _protector.Protect(plaintext);
|
||||
}
|
||||
|
||||
public string Decrypt(string ciphertext)
|
||||
{
|
||||
if (string.IsNullOrEmpty(ciphertext))
|
||||
return ciphertext;
|
||||
if (!IsEncrypted(ciphertext))
|
||||
return ciphertext; // migration transition: plaintext rows still readable
|
||||
return _protector.Unprotect(ciphertext);
|
||||
}
|
||||
|
||||
public bool IsEncrypted(string value) =>
|
||||
value.StartsWith("CfDJ8", StringComparison.Ordinal) || // DataProtection prefix
|
||||
value.Length > 50; // heuristic for protected payloads
|
||||
|
||||
public string ComputeNameSearchToken(string firstName, string lastName)
|
||||
{
|
||||
var normalized = $"{firstName.Trim().ToLowerInvariant()}|{lastName.Trim().ToLowerInvariant()}";
|
||||
using var hmac = new HMACSHA256(_searchKey);
|
||||
var hash = hmac.ComputeHash(Encoding.UTF8.GetBytes(normalized));
|
||||
return Convert.ToHexString(hash).ToLowerInvariant();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user