How can I encrypt user settings (such as passwords) in my application?

19,850

Solution 1

David, I thought your answer was nifty, but I thought those would be niftier as extension methods. That would allow such syntax as:

string cypherText;
string clearText;

using (var secureString = "Some string to encrypt".ToSecureString())
{
    cypherText = secureString.EncryptString();
}

using (var secureString = cypherText.DecryptString())
{
    clearText = secureString.ToInsecureString();
}

Here's the updated code:

using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Security;
using System.Security.Cryptography;
using System.Text;

public static class SecureIt
{
    private static readonly byte[] entropy = Encoding.Unicode.GetBytes("Salt Is Not A Password");

    public static string EncryptString(this SecureString input)
    {
        if (input == null)
        {
            return null;
        }

        var encryptedData = ProtectedData.Protect(
            Encoding.Unicode.GetBytes(input.ToInsecureString()),
            entropy,
            DataProtectionScope.CurrentUser);

        return Convert.ToBase64String(encryptedData);
    }

    public static SecureString DecryptString(this string encryptedData)
    {
        if (encryptedData == null)
        {
            return null;
        }

        try
        {
            var decryptedData = ProtectedData.Unprotect(
                Convert.FromBase64String(encryptedData),
                entropy,
                DataProtectionScope.CurrentUser);

            return Encoding.Unicode.GetString(decryptedData).ToSecureString();
        }
        catch
        {
            return new SecureString();
        }
    }

    public static SecureString ToSecureString(this IEnumerable<char> input)
    {
        if (input == null)
        {
            return null;
        }

        var secure = new SecureString();

        foreach (var c in input)
        {
            secure.AppendChar(c);
        }

        secure.MakeReadOnly();
        return secure;
    }

    public static string ToInsecureString(this SecureString input)
    {
        if (input == null)
        {
            return null;
        }

        var ptr = Marshal.SecureStringToBSTR(input);

        try
        {
            return Marshal.PtrToStringBSTR(ptr);
        }
        finally
        {
            Marshal.ZeroFreeBSTR(ptr);
        }
    }
}

Solution 2

The following is about as simple as it gets, assuming you really just want to be able to encrypt/decrypt a string and store it to disk. Note this doesn't use a password, it uses the security context of the logged in user System.Security.Cryptography.DataProtectionScope.CurrentUser to secure the data.

public class SecureIt
{
    static byte[] entropy = System.Text.Encoding.Unicode.GetBytes("Salt Is Not A Password");

    public static string EncryptString(System.Security.SecureString input)
    {
        byte[] encryptedData = System.Security.Cryptography.ProtectedData.Protect(
            System.Text.Encoding.Unicode.GetBytes(ToInsecureString(input)),
            entropy,
            System.Security.Cryptography.DataProtectionScope.CurrentUser);
        return Convert.ToBase64String(encryptedData);
    }

    public static SecureString DecryptString(string encryptedData)
    {
        try
        {
            byte[] decryptedData = System.Security.Cryptography.ProtectedData.Unprotect(
                Convert.FromBase64String(encryptedData),
                entropy,
                System.Security.Cryptography.DataProtectionScope.CurrentUser);
            return ToSecureString(System.Text.Encoding.Unicode.GetString(decryptedData));
        }
        catch
        {
            return new SecureString();
        }
    }

    public static SecureString ToSecureString(string input)
    {
        SecureString secure = new SecureString();
        foreach (char c in input)
        {
            secure.AppendChar(c);
        }
        secure.MakeReadOnly();
        return secure;
    }

    public static string ToInsecureString(SecureString input)
    {
        string returnValue = string.Empty;
        IntPtr ptr = System.Runtime.InteropServices.Marshal.SecureStringToBSTR(input);
        try
        {
            returnValue = System.Runtime.InteropServices.Marshal.PtrToStringBSTR(ptr);
        }
        finally
        {
            System.Runtime.InteropServices.Marshal.ZeroFreeBSTR(ptr);
        }
        return returnValue;
    }

}

Then to encrypt a string:

  var clearText = "Some string to encrypt";
  var cypherText = SecureIt.EncryptString( SecureIt.ToSecureString( clearText));

And to subsequently decrypt:

var clearText = SecureIt.ToInsecureString( SecureIt.DecryptString(cypherText));

Solution 3

For my purpose I modify Jesse C. Slicer solution to not use SecureString as it's not important for me to protect memory. I just need to serialize encrypted strings. To make this work project need to have reference to System.Security (not only using statement).

public static class StringSecurityHelper
{
    private static readonly byte[] entropy = Encoding.Unicode.GetBytes("5ID'&mc %sJo@lGtbi%n!G^ fiVn8 *tNh3eB %rDaVijn!.c b");

    public static string EncryptString(this string input)
    {
        if (input == null)
        {
            return null;
        }

        byte[] encryptedData = ProtectedData.Protect(Encoding.Unicode.GetBytes(input), entropy, DataProtectionScope.CurrentUser);

        return Convert.ToBase64String(encryptedData);
    }

    public static string DecryptString(this string encryptedData)
    {
        if (encryptedData == null)
        {
            return null;
        }

        try
        {
            byte[] decryptedData = ProtectedData.Unprotect(Convert.FromBase64String(encryptedData), entropy, DataProtectionScope.CurrentUser);

            return Encoding.Unicode.GetString(decryptedData);
        }
        catch
        {
            return null;
        }
    }
}

And to use it:

string cypherText = "My string".EncryptString();
string clearText = cypherText.DecryptString();
Share:
19,850

Related videos on Youtube

Odys
Author by

Odys

Excellence, is not an act, but a habit.

Updated on June 04, 2022

Comments

  • Odys
    Odys over 1 year

    I want to provide the user the ability to save personal data encrypted. This might be trivial or might have been already asked, but I am not able to find an example of an easy to use way to encrypt/decrypt a password.

    I really don't need any super-magic-unbreakable password. I just need the password to be like difficult to break.

    I've seen some MSDN and SO questions but haven't found something to use.

    • Cody Gray
      Cody Gray almost 12 years
    • Cody Gray
      Cody Gray almost 12 years
      Do note that whatever you do, you do not decrypt passwords. Rather, you ensure that the two hashes match.
    • Odys
      Odys almost 12 years
      @CodyGray is there a way to send the hashed password in the sql connection string?
    • Cody Gray
      Cody Gray almost 12 years
      Don't know anything about SQL, but I'm sure there must be. A hashed password would be exactly like any other text or numeric value.
    • Chris Shain
      Chris Shain almost 12 years
      You don't want to use user passwords in connection strings. Assuming that you are on SQL Server, doing that prevents the connections from being pooled and so decreases performance. Instead, have one account that connects to SQL Server using Windows authentication.
  • David Clarke
    David Clarke almost 12 years
    Nice improvement. I copied the code from an old unit test project where I was using actual user accounts and passwords for testing. I wanted to store the passwords securely so I could run the tests without having to display or record passwords in cleartext.
  • Matt
    Matt over 7 years
    If you decrypt a secure string there's no point to using a secure string. The whole purpose is to keep the full string out of a single block of memory. You're no better off using this method than you would be if you just took a string and encrypted it: A SecureString object should never be constructed from a String, because the sensitive data is already subject to the memory persistence consequences of the immutable String class. The best way to construct a SecureString object is from a character-at-a-time unmanaged source, such as the Console.ReadKey method.
  • Matt
    Matt over 7 years
    See the note I left below on @DavidClarke 's answer, and see this relevant msdn link
  • David Clarke
    David Clarke over 7 years
    That's a good point. I'm not suggesting that all the methods should be used in the same application. I was using a separate console app to capture the string and encrypt it prior to storing it securely in the settings for a unit test project. The intention was to provide sufficient security for the context in which it was being used.
  • Robert F.
    Robert F. over 3 years
    Doesn't ToInsecureString defeat the purpose of using SecureStrings? If you convert the secret to a string and have it in memory, you might as well start with the simple string.