using System;
using System.Security.Cryptography;
using System.Text;
namespace ivf_tl_Operate.Helpers
{
///
/// M5-02-1:凭据加密工具(DPAPI / ProtectedData,DataProtectionScope.LocalMachine)。
/// 设计要点(以 M5 子计划 M5-02-1 为准):
/// - 密文以前缀 ("enc:") 标记,Base64 存于 App.config,便于区分明文(旧值)/密文。
/// - 读取边界:IsEncrypted=true 则解密;否则视为旧明文,由调用方触发一次"读后回写为密文"迁移(幂等)。
/// - 零外部密钥管理,与本机绑定,契合"本机凭据"语义。
/// [D8] 若需跨机可迁移密钥(DPAPI LocalMachine 不可跨机),改用对称密钥+固定盐,待确认。
/// [M7] 加解密往返一致性需运行环境验证(本地不可构建/运行)。
///
public static class CryptoHelper
{
/// 密文前缀标记,用于区分明文旧值与已加密值。
public const string EncPrefix = "enc:";
// 附加熵(与本机+应用绑定,非密钥;DPAPI 主密钥由 Windows 管理)。
private static readonly byte[] Entropy = Encoding.UTF8.GetBytes("ivf_tl_operate_M5_02");
///
/// 判断给定值是否已为本工具加密的密文(带 enc: 前缀)。空值视为未加密。
///
public static bool IsEncrypted(string value)
{
return !string.IsNullOrEmpty(value) && value.StartsWith(EncPrefix, StringComparison.Ordinal);
}
///
/// 加密明文 → "enc:" + Base64(DPAPI密文)。空/已加密值原样返回(幂等)。
///
public static string Encrypt(string plain)
{
if (string.IsNullOrEmpty(plain)) return plain;
if (IsEncrypted(plain)) return plain; // 幂等:已是密文不再二次加密
try
{
byte[] data = Encoding.UTF8.GetBytes(plain);
byte[] cipher = ProtectedData.Protect(data, Entropy, DataProtectionScope.LocalMachine);
return EncPrefix + Convert.ToBase64String(cipher);
}
catch
{
// 加密失败时不写入半成品,回退原值(由 [M7] 运行环境核查)。
return plain;
}
}
///
/// 解密 "enc:"+Base64 密文 → 明文。若入参为明文(旧值)则原样返回,便于平滑迁移。
///
public static string Decrypt(string cipher)
{
if (string.IsNullOrEmpty(cipher)) return cipher;
if (!IsEncrypted(cipher)) return cipher; // 旧明文:直接返回,调用方负责回写迁移
try
{
string b64 = cipher.Substring(EncPrefix.Length);
byte[] data = Convert.FromBase64String(b64);
byte[] plain = ProtectedData.Unprotect(data, Entropy, DataProtectionScope.LocalMachine);
return Encoding.UTF8.GetString(plain);
}
catch
{
// 解密失败(如跨机迁移 [D8])回退原始字符串,避免登录流程抛异常。
return cipher;
}
}
}
}