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; } } } }