| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273 |
- using System;
- using System.Security.Cryptography;
- using System.Text;
- namespace ivf_tl_Operate.Helpers
- {
- /// <summary>
- /// M5-02-1:凭据加密工具(DPAPI / ProtectedData,DataProtectionScope.LocalMachine)。
- /// 设计要点(以 M5 子计划 M5-02-1 为准):
- /// - 密文以前缀 <see cref="EncPrefix"/>("enc:") 标记,Base64 存于 App.config,便于区分明文(旧值)/密文。
- /// - 读取边界:IsEncrypted=true 则解密;否则视为旧明文,由调用方触发一次"读后回写为密文"迁移(幂等)。
- /// - 零外部密钥管理,与本机绑定,契合"本机凭据"语义。
- /// [D8] 若需跨机可迁移密钥(DPAPI LocalMachine 不可跨机),改用对称密钥+固定盐,待确认。
- /// [M7] 加解密往返一致性需运行环境验证(本地不可构建/运行)。
- /// </summary>
- public static class CryptoHelper
- {
- /// <summary>密文前缀标记,用于区分明文旧值与已加密值。</summary>
- public const string EncPrefix = "enc:";
- // 附加熵(与本机+应用绑定,非密钥;DPAPI 主密钥由 Windows 管理)。
- private static readonly byte[] Entropy = Encoding.UTF8.GetBytes("ivf_tl_operate_M5_02");
- /// <summary>
- /// 判断给定值是否已为本工具加密的密文(带 enc: 前缀)。空值视为未加密。
- /// </summary>
- public static bool IsEncrypted(string value)
- {
- return !string.IsNullOrEmpty(value) && value.StartsWith(EncPrefix, StringComparison.Ordinal);
- }
- /// <summary>
- /// 加密明文 → "enc:" + Base64(DPAPI密文)。空/已加密值原样返回(幂等)。
- /// </summary>
- 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;
- }
- }
- /// <summary>
- /// 解密 "enc:"+Base64 密文 → 明文。若入参为明文(旧值)则原样返回,便于平滑迁移。
- /// </summary>
- 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;
- }
- }
- }
- }
|