我希望将SecureString添加到PasswordBox ,而不会泄露.Password 。可以这样做吗?例如:
tbPassword.SecurePassword = DecryptString(Properties.Settings.Default.proxyPassword);
在这种情况下,DecryptString会生成SecureString。但是,SecurePassword是一个只读属性,因此我无法为其赋值。
答案 0 :(得分:10)
你做不到。
但是,你可以做的是将占位符文本放在它的位置(它甚至可以是"placeholder"
,我们只用它来制作一些点以显示在框中)。
将占位符放入后,当您在程序中的某处检索“当前密码”时,首先检查自您输入占位符密码后PasswordChanged
事件是否已触发。如果事件未被触发,请使用旧的存储密码,如果事件已触发,请使用PasswordBox
{{1}}属性中的当前密码。
答案 1 :(得分:1)
很抱歉添加晚了,但对于也走进这里的人来说,这可能是一个想法。 (至少我在 2021 年在这个页面上找到了它)
查看PasswordBox的源码,我们可以看到它的属性是如何实现的。 Password 属性设置器只是将 String 复制到临时 SecureString 并将其转发到其内部存储。 只读 SecurePassword 属性返回内部 SecureString 的副本,因此如果尚未对其调用 .MakeReadonly(),则对其调用 .Clear() / .AppendChar(char) 只会更改此副本。
[TemplatePart(Name = "PART_ContentHost", Type = typeof (FrameworkElement))]
public sealed class PasswordBox : Control, ITextBoxViewHost
{
public SecureString SecurePassword => this.TextContainer.GetPasswordCopy();
[DefaultValue("")]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public unsafe string Password
{
[SecurityCritical] get { /* left out to reduce space */ }
[SecurityCritical] set
{
if (value == null)
value = string.Empty;
// We want to replicate this, but copy a SecureString instead of creating one from a String
using (SecureString secureString = new SecureString())
{
for (int index = 0; index < value.Length; ++index)
secureString.AppendChar(value[index]);
this.SetSecurePassword(secureString);
}
}
}
}
这可能有点麻烦,但调用私有 SetSecurePassword 可能最接近绕过转换为明文以使用密码设置器 :(我们像在 .Password 设置器中一样制作临时副本,因为我们不负责用于管理所提供 SecureString 的生命周期,甚至可以是只读的)
// option 1: streight reflection
var setPasswordMethod = typeof(PasswordBox).GetMethod("SetSecurePassword", BindingFlags.NonPublic | BindingFlags.Instance, null, new[] {typeof(SecureString)}, null);
using (var copy = mySecurePassword.Copy())
setPasswordMethod.Invoke(PasswordControl, new[] {copy});
// option 2: compiled delegate so reflection will only kick in once
Action<PasswordBox, SecureString> setSecurePassword = null; // this would be a cache lookup instead of a local variable.
if (setSecurePassword == null)
{
var passwordBox = Expression.Parameter(typeof(PasswordBox), "passwordBox");
var password = Expression.Parameter(typeof(SecureString), "securePassword");
//// if we want to include code for making the temporary copy in the delegate, use this instead to create its body
//var passwordCopy = Expression.Variable(typeof(SecureString));
//var makePasswordCopy = Expression.Call(password, nameof(SecureString.Copy), Type.EmptyTypes);
//var body = Expression.Block(new[] {passwordCopy},
// Expression.Assign(passwordCopy, makePasswordCopy),
// Expression.TryFinally(
// Expression.Call(passwordBox, "SetSecurePassword", Type.EmptyTypes, passwordCopy),
// Expression.Call(Expression.Convert(passwordCopy, typeof(IDisposable)),
// nameof(IDisposable.Dispose), Type.EmptyTypes)));
var body = Expression.Call(passwordBox, "SetSecurePassword", Type.EmptyTypes, password);
setSecurePassword = Expression.Lambda<Action<PasswordBox, SecureString>>(body, passwordBox, password).Compile();
}
using (var copy = mySecurePassword.Copy()) // if we would make the copy inside the delegate, we won't need to do it here.
setSecurePassword(PasswordControl, copy);
我希望这仍然可以帮助任何人。