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

我正在制作一个 Windows 应用程序,你需要先登录它。
帐户详细信息由用户名和密码组成,需要在本地保存它们。
这只是一个安全问题,所以其他人使用同一台电脑不能看到每个人的个人数据。
保存这些数据的最佳/最安全的方法是什么?

我不想使用数据库,所以我尝试了一些资源文件。
但是因为我是个新手,我不是很确定我在做什么以及我应该在哪里寻找解决方案。

146352 次浏览

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

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

这个链接将是很大的帮助,我希望: 点击这里

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

如果您只是要验证/验证输入的用户名和密码,请使用 Rfc2898派生字节类(也称为基于密码的密钥导出函数2或 PBKDF2)。这比使用像 Triple DES 或 AES 这样的加密更安全,因为没有实际的方法从 RFC2898派生字节返回到密码。您只能从密码转到结果。请参阅 当从密码字符串派生加密密钥和 IV 时,是否可以使用密码的 SHA1散列作为 salt?以获得示例并讨论。用于 WinRT/Metro 的 Net 或 字符串加密/解密密码 c # Metro Style

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

在 C # 中,使用 系统。安全。加密。受保护的数据类。例如,要加密一段数据,使用 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);

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

byte[] plaintext= ProtectedData.Unprotect(ciphertext, entropy,
DataProtectionScope.CurrentUser);

请注意,还有其他的安全考虑。例如,避免将密码等机密存储为 string。字符串是不可变的,因为不能在内存中通知它们,所以查看应用程序内存或内存转储的人可能会看到密码。使用 SecureString或一个字节[]来代替,并记住在不再需要密码时立即释放或清零它们。

这只适用于 Windows,所以如果你计划使用 dotnet 核心跨平台,你将不得不寻找其他地方。参见 https://github.com/dotnet/corefx/blob/master/Documentation/architecture/cross-platform-cryptography.md

我想加密和解密字符串作为一个可读的字符串。

下面是 C # VisualStudio2019WinForms 中基于 @Pradip答案的一个非常简单的快速示例。

右键单击 project > properties > sets > Create a username and password set。

enter image description here

现在您可以利用刚才创建的那些设置。这里我保存的 usernamepassword,但只加密的 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>

enter image description here

全密码

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

对于简单的场景,还可以使用使用 C # 包装器 证书管理的 Windows 凭据管理 API。它提供了单一的地方存储/检索密码,易于更改。

Https://stackoverflow.com/a/32550674/1129978