EncryptedConfigValue.cs 1.8 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647
  1. namespace Application.Abstractions.Crypto;
  2. /// <summary>
  3. /// Config 문자열 컬럼에 키 버전을 함께 담아 암호화 저장하는 헬퍼.
  4. /// 별도 KeyVersion 컬럼이 없는 Config 특성상 "enc:v{version}:{Base64}" 자체 포맷을 사용한다.
  5. /// 접두사가 없는 값은 레거시 평문으로 간주해 그대로 반환한다 (하위호환).
  6. /// </summary>
  7. public static class EncryptedConfigValue
  8. {
  9. private const string Prefix = "enc:v";
  10. /// <summary>평문을 현재 키 버전으로 암호화해 버전 접두사 포맷으로 반환</summary>
  11. public static string Protect(IFieldEncryptor encryptor, string plaintext)
  12. {
  13. ArgumentNullException.ThrowIfNull(encryptor);
  14. return $"{Prefix}{encryptor.CurrentKeyVersion}:{encryptor.Encrypt(plaintext)}";
  15. }
  16. /// <summary>버전 접두사 포맷 여부</summary>
  17. public static bool IsProtected(string? value)
  18. {
  19. return value is not null && value.StartsWith(Prefix, StringComparison.Ordinal);
  20. }
  21. /// <summary>버전 접두사 포맷이면 복호화, 아니면(레거시 평문) 그대로 반환</summary>
  22. public static string Unprotect(IFieldEncryptor encryptor, string value)
  23. {
  24. ArgumentNullException.ThrowIfNull(encryptor);
  25. ArgumentNullException.ThrowIfNull(value);
  26. if (!IsProtected(value))
  27. {
  28. return value;
  29. }
  30. var rest = value.AsSpan(Prefix.Length);
  31. var separator = rest.IndexOf(':');
  32. if (separator <= 0 || !int.TryParse(rest[..separator], out var version))
  33. {
  34. throw new InvalidOperationException("암호화된 설정 값의 키 버전 형식이 올바르지 않습니다.");
  35. }
  36. return encryptor.Decrypt(rest[(separator + 1)..].ToString(), version);
  37. }
  38. }