如何安全保存用户名/密码(本地)?

Rob*_*bin 98 c# security local

我正在制作一个Windows应用程序,您需要先登录.
帐户详细信息由用户名和密码组成,需要在本地保存.
这只是一个安全问题,因此使用同一台计算机的其他人无法看到每个人的个人数据.
保存此数据的最佳/最安全的方法是什么?

我不想使用数据库,所以我尝试了一些资源文件.
但由于我对此有点新意,我不完全确定我在做什么以及我应该在哪里寻找解决方案.

akt*_*ton 149

如果您要验证/验证输入的用户名和密码,请使用Rfc2898DerivedBytes类(也称为基于密码的密钥派生函数2或PBKDF2).这比使用Triple DES或AES之类的加密更安全,因为没有实际的方法可以将RFC2898DerivedBytes的结果返回到密码.您只能从密码到结果.从密码字符串中导出加密密钥和IV时,请参阅使用密码的SHA1哈希作为salt吗?有关.Net或String的示例和讨论,使用密码c#Metro Style for WinRT/Metro 加密/解密.

如果要存储密码以供重用,例如将其提供给第三方,请使用Windows Data Protection API(DPAPI).这使用操作系统生成和保护的密钥以及Triple DES加密算法来加密和解密信息.这意味着您的应用程序不必担心生成和保护加密密钥,这是使用加密技术时的一个主要问题.

在C#中,使用System.Security.Cryptography.ProtectedData类.例如,要加密一段数据,请使用ProtectedData.Protect():

// Data to protect. Convert a string to a byte[] using Encoding.UTF8.GetBytes().
byte[] plaintext; 

// Generate additional entropy (will be used as the Initialization vector)
byte[] entropy = new byte[20];
using(RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider())
{
    rng.GetBytes(entropy);
}

byte[] ciphertext = ProtectedData.Protect(plaintext, entropy,
    DataProtectionScope.CurrentUser);
Run Code Online (Sandbox Code Playgroud)

安全地存储熵和密文,例如在具有权限设置的文件或注册表项中,以便只有当前用户可以读取它.要访问原始数据,请使用ProtectedData.Unprotect():

byte[] plaintext= ProtectedData.Unprotect(ciphertext, entropy,
    DataProtectionScope.CurrentUser);
Run Code Online (Sandbox Code Playgroud)

请注意,还有其他安全注意事项.例如,避免将密码等密码存储为string.字符串是不可变的,因为它们无法在内存中通知,因此查看应用程序内存或内存转储的人可能会看到密码.请使用SecureString或byte [],并记住在不再需要密码时立即处理或清零它们.

  • 信息量很大,但是我认为使用 `ProtectedData` 的全部意义在于我不需要担心 *安全地存储熵和密文,......所以只有当前用户才能阅读它*。我认为它提供了简单性,因为我可以存储它们但是很方便,并且仍然只有 CurrentUser 可以解密它。`entropy` 参数也是可选的,看起来类似于唯一性比保密更重要的 IV。因此,在明文的变化和更新不频繁的情况下,该值可能会被省略或硬编码到程序中。 (4认同)
  • 似乎该类现在称为Rfc2898DeriveBytes(小写字母,.net 4.5和4.6),可在此处找到:命名空间:System.Security.Cryptography程序集:mscorlib(在mscorlib.dll中) (2认同)
  • 事情发生了变化,现在从安全角度来看 SecureString 已被弃用:https://github.com/dotnet/platform-compat/blob/master/docs/DE0001.md (2认同)
  • @mCasamento - 确实如此,但对于非关键应用程序来说,这可能仍然是一个实际的权衡。因为替换并不总是方便(来自该链接):*“处理凭据的一般方法是避免它们,而是依赖其他方式进行身份验证,例如证书或 Windows 身份验证。”* (2认同)

Pra*_*dip 9

我之前使用过这个,我认为为了确保凭证能够持续存在并以最安全的方式存在

  1. 您可以使用ConfigurationManager该类将它们写入app配置文件
  2. 使用SecureString课程保护密码
  3. 然后使用Cryptography命名空间中的工具加密它.

这个链接对我希望有很大的帮助:点击这里


swi*_*tgp 5

DPAPI 就是为了这个目的。使用DPAPI对用户第一次输入的密码进行加密,存储在安全的位置(用户的注册表,用户的应用程序数据目录,是一些选择)。每当应用程序启动时,检查位置以查看您的密钥是否存在,如果它确实使用 DPAPI 对其进行解密并允许访问,否则拒绝它。


Jon*_*nas 5

我想将字符串加密和解密为可读字符串。

这是 C# Visual Studio 2019 WinForms 中一个非常简单的快速示例,基于@Pradip.

右键单击项目 > 属性 > 设置 > 创建usernamepassword设置。

在此处输入图片说明

现在,您可以利用刚刚创建的那些设置。在这里,我保存username并且password只加密文件password中它的可观值字段user.config

user.config文件中加密字符串的示例。

<?xml version="1.0" encoding="utf-8"?>
<configuration>
    <userSettings>
        <secure_password_store.Properties.Settings>
            <setting name="username" serializeAs="String">
                <value>admin</value>
            </setting>
            <setting name="password" serializeAs="String">
                <value>AQAAANCMnd8BFdERjHoAwE/Cl+sBAAAAQpgaPYIUq064U3o6xXkQOQAAAAACAAAAAAAQZgAAAAEAACAAAABlQQ8OcONYBr9qUhH7NeKF8bZB6uCJa5uKhk97NdH93AAAAAAOgAAAAAIAACAAAAC7yQicDYV5DiNp0fHXVEDZ7IhOXOrsRUbcY0ziYYTlKSAAAACVDQ+ICHWooDDaUywJeUOV9sRg5c8q6/vizdq8WtPVbkAAAADciZskoSw3g6N9EpX/8FOv+FeExZFxsm03i8vYdDHUVmJvX33K03rqiYF2qzpYCaldQnRxFH9wH2ZEHeSRPeiG</value>
            </setting>
        </secure_password_store.Properties.Settings>
    </userSettings>
</configuration>
Run Code Online (Sandbox Code Playgroud)

在此处输入图片说明

完整代码

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Security;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace secure_password_store
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void Exit_Click(object sender, EventArgs e)
        {
            Application.Exit();
        }

        private void Login_Click(object sender, EventArgs e)
        {
            if (checkBox1.Checked == true)
            {
                Properties.Settings.Default.username = textBox1.Text;
                Properties.Settings.Default.password = EncryptString(ToSecureString(textBox2.Text));
                Properties.Settings.Default.Save();
            }
            else if (checkBox1.Checked == false)
            {
                Properties.Settings.Default.username = "";
                Properties.Settings.Default.password = "";
                Properties.Settings.Default.Save();
            }
            MessageBox.Show("{\"data\": \"some data\"}","Login Message Alert",MessageBoxButtons.OK, MessageBoxIcon.Information);
        }
        private void DecryptString_Click(object sender, EventArgs e)
        {
            SecureString password = DecryptString(Properties.Settings.Default.password);
            string readable = ToInsecureString(password);
            textBox4.AppendText(readable + Environment.NewLine);
        }
        private void Form_Load(object sender, EventArgs e)
        {
            //textBox1.Text = "UserName";
            //textBox2.Text = "Password";
            if (Properties.Settings.Default.username != string.Empty)
            {
                textBox1.Text = Properties.Settings.Default.username;
                checkBox1.Checked = true;
                SecureString password = DecryptString(Properties.Settings.Default.password);
                string readable = ToInsecureString(password);
                textBox2.Text = readable;
            }
            groupBox1.Select();
        }


        static byte[] entropy = Encoding.Unicode.GetBytes("SaLtY bOy 6970 ePiC");

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

        public static SecureString DecryptString(string encryptedData)
        {
            try
            {
                byte[] decryptedData = ProtectedData.Unprotect(Convert.FromBase64String(encryptedData),entropy,DataProtectionScope.CurrentUser);
                return ToSecureString(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;
        }

        private void EncryptString_Click(object sender, EventArgs e)
        {
            Properties.Settings.Default.password = EncryptString(ToSecureString(textBox2.Text));
            textBox3.AppendText(Properties.Settings.Default.password.ToString() + Environment.NewLine);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)