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

C# 如何安全地保存用户名/密码(本地)?,c#,security,local,C#,Security,Local,我正在制作一个Windows应用程序,您需要先登录它。 帐户详细信息由用户名和密码组成,需要保存在本地。 这只是一个安全问题,因此使用同一台计算机的其他人无法查看每个人的个人数据。 保存此数据的最佳/最安全的方法是什么? 我不想使用数据库,所以我尝试了使用资源文件的一些方法。 但由于我对这方面有点陌生,我不完全确定我在做什么,以及我应该在哪里寻找解决方案。 我以前使用过这个,我认为为了确保凭证持久并以最佳安全的方式使用 您可以使用ConfigurationManager类将它们写入应用程序配

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

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

我以前使用过这个,我认为为了确保凭证持久并以最佳安全的方式使用

  • 您可以使用
    ConfigurationManager
    类将它们写入应用程序配置文件
  • 使用
    SecureString
    类保护密码
  • 然后使用
    加密
    命名空间中的工具对其进行加密

  • 我希望这个链接会有很大帮助:

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

    如果您只是要验证/验证输入的用户名和密码,请使用该类(也称为基于密码的密钥派生函数2或PBKDF2)。这比使用三重DES或AES之类的加密更安全,因为从RFC2898DerivedBytes的结果返回密码没有实际的方法。您只能从密码转到结果。有关.Net或WinRT/Metro的示例和讨论,请参见

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

    在C#中,使用类。例如,要加密一段数据,请使用:

    安全地存储熵和密文,例如在设置了权限的文件或注册表项中,以便只有当前用户才能读取。要访问原始数据,请使用:


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

    这只适用于Windows,因此如果您计划使用dotnet core跨平台,您必须寻找其他地方。请参见

    我想将字符串作为可读字符串进行加密和解密

    下面是一个基于
    @Pradip
    答案的C#Visual Studio 2019 WinForms中非常简单的快速示例

    右键单击项目>属性>设置>创建
    用户名
    密码
    设置

    现在,您可以利用刚刚创建的设置。在这里,我保存了
    用户名
    密码
    ,但只在
    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>
    
    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>
    

    首先,不要保存密码。散列它(可能带有salt值),然后保存它。“用户”是指普通的Windows用户还是其他什么?(我想你们的意思是你们中的一些人拥有“用户”,因为普通的Windows用户已经看不到彼此的数据了…)我已经编辑了你们的标题。请看“”,其中的共识是“不,他们不应该”。@John Saunders好吧,请原谅我的无知。有完整源代码的最终解决方案吗?嗨,我试过了,但在entropy=rng时出错。GetBytes(20)说:无法从int转换为byte[]这个类现在被称为Rfc2898DeriveBytes(小写字母,.net 4.5和4.6)可以在这里找到:Namespace:System.Security.Cryptography Assembly:mscorlib(在mscorlib.dll中)非常有用,但是我认为使用
    ProtectedData
    的全部意义在于,我不需要担心安全地存储熵和密文。。。因此,只有当前用户才能读取它。我认为它提供了简单性,因为我可以存储它们,但是很方便,仍然只有当前用户可以解密它。
    entropy
    参数也是可选的,看起来类似于IV,其中唯一性比保密性更重要。因此,在纯文本的变化和更新不频繁的情况下,该值可能会被忽略或硬编码到程序中。情况发生了变化,现在从安全角度来看SecureString已被弃用:@mCasamento-true,但对于非关键应用程序,这可能仍然是一个实际的权衡。因为替换并不总是很方便(从该链接):“处理凭据的一般方法是避免使用凭据,而是依赖其他方式进行身份验证,如证书或Windows身份验证。”此链接已失效(404),这正是我需要的。我的要求是将符合FIPS的Visio加载项的密码信息保存到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>
    
    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);
            }
        }
    }