持久化到磁盘时是否必须加密 SecureString?

Do I have to encrypt SecureString when persisting to disk?

对于 C# 控制台应用程序,我需要在应用程序设置中保留密码,但是当我创建 System.Security.SecureString 类型的设置时,设置本身会从纯文本配置文件中删除。由于我无法再看到原始值,因此我无法验证数据在保存时是否仍处于加密状态。

SecureString 是最好的方法还是我应该使用 ProtectedData 来简单地加密字符串?

--编辑-- 这是我用来验证 SecureString 可以持久化的测试代码。

        [global::System.Configuration.ApplicationScopedSettingAttribute()]
        [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
        public global::System.Security.SecureString Password
        {
            get
            {
                return ((global::System.Security.SecureString)(this["Password"]));
            }
            set { this["Password"] = value; }
        }

        static void Main(string[] args)
        {
            PersistPassword("A-Test-Password");
            Console.WriteLine(ReadPassword());
            Console.ReadLine();
        }

        static void PersistPassword(string Password)
        {
            SecureString ss = new SecureString();
            Password.ToCharArray().ToList().ForEach(ss.AppendChar);
            Settings.Default.Password = ss;
        }

        static string ReadPassword()
        {
            SecureString ss = Settings.Default.Password;
            IntPtr ptr = Marshal.SecureStringToCoTaskMemUnicode(ss);
            return Marshal.PtrToStringUni(ptr);
        }

MSDN

中所述

The value of an instance of SecureString is automatically protected using a mechanism supported by the underlying platform when the instance is initialized or when the value is modified.

如果您想提供一种机制,一旦密码存储在 securestring 中就保持只读状态,那么您可以对其调用 MarkAsReadonly() 方法。

出于持久性目的,您还可以散列 SecureString 并为其创建盐。您可以取回盐以备后用,例如比较目的。查看 this 代码片段,它在 Securestring.

上使用盐

您无法保存使用 SecureString 加密的数据。密钥保存在内存中,并且只在您的程序处于活动状态时存在。 SecureString 是原生 CryptProtectMemory 函数的包装器。

如果您需要加密数据持久化(比您的程序存在的时间更长),您需要 数据保护 API (DPAPI),它是 CryptProtectData 函数 - 通过 ProtectedData class.

向 C# 用户公开

SecureString 的优点是短暂;适用于:

  • 密码
  • 信用卡号码
  • 社会保险号码

当它们被您的程序使用时 - 然后被删除。

DPAPI 更适合 long-term 存储。但要注意保护级别,并选择适合您需要的级别:

  • 只有我能解密
  • 只能在此 PC 上解密
  • 只能由域中的任何人解密

如果您需要能够传输到不同站点或不同域的加密:CryptProtectData 不适合您。

四个级别

  • CryptProtectMemory(.NET 中的 SecureString):仅在进程内存中有用
  • CryptProtectData(.NET 中的 ProtectedData):加密数据块,您可以将其存储在任何您喜欢的地方(内存、注册表、硬盘)——但您必须自己收藏。
  • CredWrite: 使用 CryptProtectData 加密密码,并将其存储在 Windows 密码库中(开始 →凭证管理器)
  • CredUIPromptForCredentials/CredUIConfirmCredentials:提示用户输入密码,加密后保存在密码库中(开始→凭证经理)使用 CredWrite