【问题标题】:How can I encrypt user settings (such as passwords) in my application?如何在我的应用程序中加密用户设置(例如密码)?
【发布时间】:2012-01-15 16:36:24
【问题描述】:

我想为用户提供加密保存个人数据的能力。这可能是微不足道的,或者可能已经被问过了,但我找不到一个易于使用的加密/解密密码的示例。

我真的不需要任何超级魔法牢不可破的密码。我只需要密码难以破解。

我看过一些 MSDN 和 SO 问题,但没有找到可以使用的东西。

【问题讨论】:

  • 请注意,无论您做什么,您都不要解密密码。相反,您要确保两个哈希匹配。
  • @CodyGray 有没有办法在 sql 连接字符串中发送散列密码?
  • 对SQL一无所知,但我相信一定有。散列密码与任何其他文本或数值完全一样。
  • 您不想在连接字符串中使用用户密码。假设您在 SQL Server 上,这样做会阻止连接被池化,从而降低性能。相反,拥有一个使用 Windows 身份验证连接到 SQL Server 的帐户。

标签: c# .net winforms


【解决方案1】:

David,我认为your answer 很漂亮,但我认为这些作为扩展方法会更漂亮。这将允许这样的语法:

string cypherText;
string clearText;

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

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

这是更新后的代码:

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

【讨论】:

  • 不错的改进。我从一个旧的单元测试项目中复制了代码,在该项目中我使用实际的用户帐户和密码进行测试。我想安全地存储密码,这样我就可以运行测试而不必以明文形式显示或记录密码。
  • 查看我在@DavidClarke 的回答下面留下的注释,并查看相关的msdn link
  • ToInsecureString 不是违背了使用SecureStrings 的目的吗?如果您将秘密转换为string 并将其保存在内存中,您不妨从简单的string 开始。
【解决方案2】:

以下内容非常简单,假设您真的只想加密/解密字符串并将其存储到磁盘。请注意,这不使用密码,它使用登录用户 System.Security.Cryptography.DataProtectionScope.CurrentUser 的安全上下文来保护数据。

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

}

然后加密一个字符串:

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

然后解密:

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

【讨论】:

  • 如果你解密一个安全字符串,那么使用安全字符串是没有意义的。整个目的是将整个字符串保留在单个内存块之外。使用这种方法并不比你只取一个字符串并加密它更好: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.
  • 这很好。我并不是建议在同一个应用程序中使用所有方法。我使用单独的控制台应用程序来捕获字符串并对其进行加密,然后再将其安全地存储在单元测试项目的设置中。目的是为使用它的上下文提供足够的安全性。
【解决方案3】:

出于我的目的,我将Jesse C. Slicer 解决方案修改为不使用SecureString,因为保护内存对我来说并不重要。我只需要序列化加密的字符串。要使这个工作项目需要参考System.Security(不仅是 using 语句)。

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

并使用它:

string cypherText = "My string".EncryptString();
string clearText = cypherText.DecryptString();

【讨论】:

    猜你喜欢
    • 2017-01-08
    • 2022-12-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-08-31
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多