Search code examples
c#securestring

Do I have to encrypt SecureString when persisting to disk?


For a C# console app, I need to persist a password in the application settings but when I create a setting of type System.Security.SecureString the setting itself is removed from the plain-text config file. Since I can't see the raw value anymore I can't validate whether or not the data is still encrypted when saved.

Is SecureString the best approach or should I use ProtectedData to simply encrypt the string?

--EDIT-- Here is the test code that I used to validate that a SecureString can be persisted.

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

Solution

  • You cannot persist data that is encrypted with SecureString. The key is held in memory, and only lasts as long as your program is alive. SecureString is a wrapper around the native CryptProtectMemory function.

    If you need you need the encrypted data to be persistable (for longer than your program exists), you need the Data Protection API (DPAPI), and it's CryptProtectData function - which is exposed to C# users through the ProtectedData class.

    SecureString has the advantage of being ephemeral; useful for:

    • passwords
    • credit card numbers
    • Social Insurance numbers

    while they are being used by your program - and then deleted.

    The DPAPI is better for long-term storage. But beware the protection levels, and you choose the one that is appropriate for what you need:

    • only decryptable by me
    • only decryptable on this PC
    • only decryptable by anyone on the domain

    If you need encryption that can survive transport to different sites or different domains: CryptProtectData is not for you.

    Four levels

    • CryptProtectMemory (SecureString in .NET): only useful in memory of your process
    • CryptProtectData (ProtectedData in .NET): encrypts a blob of data, and you can store it anywhere you like (memory, registry, hard disk) - but you have to store it yourself.
    • CredWrite: encrypts a password using CryptProtectData, and stores it for you in the Windows Password Vault (Start → Credential Manager)
    • CredUIPromptForCredentials/CredUIConfirmCredentials: prompt the user for a password, encrypt it, and saves it in the Password Vault (Start → Credential Manager) using CredWrite