我正在制作一个Windows应用程序,首先需要登录。
账户信息包括用户名和密码,需要本地保存。
这只是为了安全起见,让使用同一台计算机的其他人无法看到所有人的个人数据。
最好/最安全的保存这些数据的方法是什么?
我不想使用数据库,所以我尝试了一些Resource文件的方法。
但由于我在这方面还比较新手,我不确定我正在做什么以及应该在哪里寻找解决方案。
我正在制作一个Windows应用程序,首先需要登录。
账户信息包括用户名和密码,需要本地保存。
这只是为了安全起见,让使用同一台计算机的其他人无法看到所有人的个人数据。
最好/最安全的保存这些数据的方法是什么?
我不想使用数据库,所以我尝试了一些Resource文件的方法。
但由于我在这方面还比较新手,我不确定我正在做什么以及应该在哪里寻找解决方案。
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或byte[],并在密码不再需要时立即处理或清零它们。'RNGCryptoServiceProvider': type used in a using statement must be implicitly convertible to 'System.IDisposable'
- 这是在完全复制您的代码之后出现的 - 您有什么建议吗?谢谢! - BassieProtectedData
的整个意义在于,我不必担心如何“安全存储熵和密文,...以便只有当前用户才能读取它”。这样,使用它可以使存储简化,并且仍然只有当前用户可以解密。 entropy
参数也是可选的,类似于IV,其中唯一性比机密性更重要。因此,在平文本的变化和更新不频繁的情况下,该值可能可以省略或硬编码到程序中。 - antak我想要将字符串加密并解密成可读的字符串。
这里是一个基于@Pradip
回答的非常简单的C# Visual Studio 2019 WinForms快速示例。
右键项目>属性>设置>创建用户名
和密码
设置。
现在您可以利用刚刚创建的那些设置。这里我保存了用户名
和密码
,但只加密了密码
在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);
}
}
}
我以前用过这个技术,我认为为了确保凭据持久存在且以最安全的方式,可以:
ConfigurationManager
类将它们写入应用程序配置文件SecureString
类保护密码Cryptography
命名空间中的工具进行加密。希望这个链接能够帮到你:点击这里
DPAPI专门用于此目的。第一次用户输入密码时,请使用DPAPI进行加密,将其存储在安全位置(例如用户注册表、用户应用程序数据目录等)。每次启动应用程序时,请检查位置以查看密钥是否存在,如果存在,请使用DPAPI解密并允许访问,否则拒绝访问。
这只适用于Windows系统,如果您计划使用跨平台的dotnet core,则需要另寻他路。请查看 https://github.com/dotnet/corefx/blob/master/Documentation/architecture/cross-platform-cryptography.md