Brian Mitchell
Brian Mitchell

Reputation: 929

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

Upvotes: 0

Views: 1189

Answers (2)

Ian Boyd
Ian Boyd

Reputation: 256991

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

Upvotes: 1

vendettamit
vendettamit

Reputation: 14687

As states in 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.

If you want to provide the mechanism to keep it readonly once the password in store in securestring then you can invoke the method MarkAsReadonly() on it.

For persistence purpose you can also Hash the SecureString and create a salt for it. You can retreive the salt for later use e.g. comparison pupose. Check out this code snippet which is using the salt over Securestring.

Upvotes: 1

Related Questions