I have an existing SecureString that I would like to put into a PasswordBox without revealing the .Password. Can this be done? For example:
tbPassword.SecurePassword = DecryptString(Properties.Settings.Default.proxyPassword);
In this case DecryptString produces a SecureString. However, SecurePassword is a read-only property so I can't assign a value to it.
I would suggest that when accessing the PasswordBox. Password CLR property you'd refrain from placing it in any variable or as a value for any property. Keeping your password in plain text on the client machine RAM is a security no-no. So get rid of that public string Password { get; set; } you've got up there.
Out of the box, the WPF PasswordBox is the go to control for getting passwords, or other sensitive information. For the sake of security, it provides a SecureString object through the SecurePassword property which is, in my case, secure enough for my needs.
Sorry for the late addition, but it might be an idea for people who also walk into this. (At least I ended up on this page in 2021 looking for it)
Looking at the source of PasswordBox, we can see how its properties are implemented. The Password property setter just copies the String into a temporary SecureString and forwards it to its internal storage. The readonly SecurePassword property returns a copy of the internal SecureString, so calling .Clear() / .AppendChar(char) on it will only change this copy, if .MakeReadonly() has not been called on it.
[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);
}
}
}
}
It may be a bit hacky, but calling the private SetSecurePassword may be closest to bypassing conversion to clear text in order to use the Password setter : (we make a temporary copy just like in the .Password setter as we are not responsible for managing lifetime of the provided SecureString, which could even be readonly)
// 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);
I hope this still helps anyone.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With